#=============================================================================== # GTEx Tissue-Level Modeling Pipeline # Purpose: Tissue-specific quality prediction using Lasso regression #===============================================================================

#—————————————————————————— # 1. Setup #——————————————————————————

# Load data once
if(!exists("merged_gtex")) {
  merged_gtex <- readRDS(file.path(data_path, "processed/merged_gtex.rds"))
}

#—————————————————————————— # 2. Within-Group vs Between-Group Analysis for Single Tissue #——————————————————————————

# Analyze a specific tissue
tissue_name <- "Esophagus"
tissue_data <- merged_gtex[tissue_main == tissue_name]
subtypes <- unique(tissue_data$tissue_sub)

if(length(subtypes) > 1 && nrow(tissue_data) >= 10) {
  # Extract and scale features
  feature_cols <- grep("^feature_", names(tissue_data), value = TRUE)
  feature_matrix <- scale(as.matrix(tissue_data[, ..feature_cols]))
  
  # Calculate distance matrix
  dist_matrix <- dist(feature_matrix)
  
  # Calculate within-group and between-group distances
  within_distances <- list()
  between_distances <- list()
  
  for(subtype in subtypes) {
    subtype_indices <- which(tissue_data$tissue_sub == subtype)
    
    if(length(subtype_indices) > 1) {
      # Within-group distances
      subtype_dist <- as.matrix(dist_matrix)[subtype_indices, subtype_indices]
      within_distances[[subtype]] <- subtype_dist[upper.tri(subtype_dist)]
      
      # Between-group distances
      other_indices <- which(tissue_data$tissue_sub != subtype)
      if(length(other_indices) > 0) {
        between_dists <- as.matrix(dist_matrix)[subtype_indices, other_indices]
        between_distances[[subtype]] <- as.vector(between_dists)
      }
    }
  }
  
  # Calculate summary statistics
  all_within <- unlist(within_distances)
  all_between <- unlist(between_distances)
  
  similarity_results <- list(
    tissue = tissue_name,
    subtypes = subtypes,
    samples_per_subtype = table(tissue_data$tissue_sub),
    summary = data.frame(
      comparison = c("Within Subtype", "Between Subtypes"),
      mean_distance = c(mean(all_within), mean(all_between)),
      median_distance = c(median(all_within), median(all_between))
    ),
    ratio = mean(all_between) / mean(all_within)
  )
  
  # PERMANOVA test
  perm_test <- adonis2(dist_matrix ~ tissue_sub, data = tissue_data)
  statistical_results <- list(
    tissue = tissue_name,
    permanova = perm_test,
    r_squared = perm_test$R2[1],
    p_value = perm_test$`Pr(>F)`[1],
    significant = perm_test$`Pr(>F)`[1] < 0.05
  )
  
  # Create visualizations
  pca_result <- prcomp(feature_matrix)
  pca_plot <- ggplot(data.frame(PC1 = pca_result$x[,1], PC2 = pca_result$x[,2], 
                                Subtype = tissue_data$tissue_sub), 
                     aes(x = PC1, y = PC2, color = Subtype)) +
    geom_point(alpha = 0.7, size = 3) +
    theme_minimal() +
    labs(title = paste("PCA of", tissue_name, "by Subtype"))
  
  ggsave(file.path(output_path, paste0(tissue_name, "_subtype_pca.pdf")), 
         pca_plot, width = 10, height = 8, dpi = 300)
  
  # UMAP visualization
  set.seed(123)
  umap_result <- uwot::umap(feature_matrix, n_neighbors = min(15, nrow(feature_matrix)-1))
  
  umap_plot <- ggplot(data.frame(UMAP1 = umap_result[,1], UMAP2 = umap_result[,2],
                                 Subtype = tissue_data$tissue_sub),
                     aes(x = UMAP1, y = UMAP2, color = Subtype)) +
    geom_point(alpha = 0.7, size = 3) +
    theme_minimal() +
    labs(title = paste("UMAP of", tissue_name, "by Subtype"))
  
  ggsave(file.path(output_path, paste0(tissue_name, "_subtype_umap.pdf")), 
         umap_plot, width = 10, height = 8, dpi = 300)
  
  # Save results
  saveRDS(similarity_results, file.path(results_path, paste0(tissue_name, "_distance_analysis.rds")))
  saveRDS(statistical_results, file.path(results_path, paste0(tissue_name, "_statistical_tests.rds")))
}

#—————————————————————————— # 3. Tissue-Level Lasso Modeling #——————————————————————————

# Set parameters
k_folds <- 5
targets <- c("SMRIN", "SMATSSCR")
tissues <- unique(merged_gtex$tissue_main)
all_results <- list()

# Process each target
for(target in targets) {
  target_results <- list()
  message(paste("\nProcessing target:", target))
  
  # Process each tissue
  for(tissue_name in tissues) {
    message(paste("\nAnalyzing", tissue_name, "for", target))
    
    # Extract and filter data
    tissue_data <- merged_gtex[tissue_main == tissue_name]
    tissue_data <- tissue_data[!is.na(tissue_data[[target]])]
    
    if(nrow(tissue_data) < 10) {
      message(paste("Insufficient samples for", tissue_name))
      next
    }
    
    # Feature selection
    feature_cols <- grep("^feature_", names(tissue_data), value = TRUE)
    feature_vars <- apply(tissue_data[, ..feature_cols], 2, var)
    valid_features <- feature_cols[feature_vars > 1e-10]
    
    # Create cross-validation folds
    set.seed(123)
    folds <- createFolds(tissue_data[[target]], k = k_folds)
    
    # Cross-validation
    fold_results <- list()
    all_fold_predictions <- data.frame()
    
    for(i in 1:k_folds) {
      # Split data
      test_indices <- folds[[i]]
      train_data <- tissue_data[-test_indices]
      test_data <- tissue_data[test_indices]
      
      # Feature selection - top 5% correlated
      feature_correlations <- sapply(valid_features, function(col) {
        cor(train_data[[col]], train_data[[target]], use = "complete.obs")
      })
      
      top_n_features <- ceiling(length(valid_features) * 0.05)
      top_features <- names(sort(abs(feature_correlations), decreasing = TRUE))[1:top_n_features]
      
      # Prepare matrices
      x_train <- as.matrix(train_data[, ..top_features])
      y_train <- train_data[[target]]
      x_train_scaled <- scale(x_train)
      x_mean <- attr(x_train_scaled, "scaled:center")
      x_sd <- attr(x_train_scaled, "scaled:scale")
      
      # Train model
      cv_fit <- cv.glmnet(x_train_scaled, y_train, alpha = 1)
      best_lambda <- cv_fit$lambda.min
      lasso_model <- glmnet(x_train_scaled, y_train, alpha = 1, lambda = best_lambda)
      
      # Make predictions
      x_test <- as.matrix(test_data[, ..top_features])
      x_test_scaled <- scale(x_test, center = x_mean, scale = x_sd)
      predictions <- predict(lasso_model, x_test_scaled, s = best_lambda)
      
      # Store predictions
      fold_predictions <- data.frame(
        fold = i,
        sample_id = test_data$sample_id,
        actual = test_data[[target]],
        predicted = as.numeric(predictions)
      )
      all_fold_predictions <- rbind(all_fold_predictions, fold_predictions)
      
      # Calculate metrics
      rmse <- sqrt(mean((predictions - test_data[[target]])^2))
      r_value <- cor(predictions, test_data[[target]]) 
      r2 <- cor(predictions, test_data[[target]])^2
      mae <- mean(abs(predictions - test_data[[target]]))
      
      fold_results[[i]] <- list(
        fold = i,
        rmse = rmse,
        r = r_value,
        r2 = r2,
        mae = mae,
        lambda = best_lambda,
        model = lasso_model,
        features = top_features
      )
    }
    
    # Calculate overall performance
    pooled_rmse <- sqrt(mean((all_fold_predictions$predicted - all_fold_predictions$actual)^2))
    pooled_r <- cor(all_fold_predictions$predicted, all_fold_predictions$actual)  # <-- R, not R²
    pooled_r2 <- cor(all_fold_predictions$predicted, all_fold_predictions$actual)^2
    pooled_mae <- mean(abs(all_fold_predictions$predicted - all_fold_predictions$actual))
    
    # Count feature selection frequency
    all_selected_features <- unlist(lapply(fold_results, function(x) x$features))
    feature_counts <- table(all_selected_features)
    consistent_features <- names(feature_counts[feature_counts >= 3])
    
    # Store results
    tissue_model_result <- list(
      tissue = tissue_name,
      target = target,
      performance = list(
        pooled_rmse = pooled_rmse,
        pooled_r = pooled_r,
        pooled_r2 = pooled_r2,
        pooled_mae = pooled_mae
      ),
      fold_results = fold_results,
      feature_counts = feature_counts,
      consistent_features = consistent_features,
      sample_size = nrow(tissue_data),
      all_predictions = all_fold_predictions
    )
    
    target_results[[tissue_name]] <- tissue_model_result
    
    # Create visualization for good models (using R > 0.55 which is approximately R² > 0.3)
  # if(pooled_r2 > 0.3) {  # use if R² is required
    if(abs(pooled_r) > 0.55) {
      scatter_plot <- ggplot(all_fold_predictions, aes(x = actual, y = predicted)) +
        geom_point(alpha = 0.6, color = "darkblue", size = 2) +
        geom_abline(intercept = 0, slope = 1, color = "red", linetype = "dashed") +
        geom_smooth(method = "lm", se = TRUE, color = "blue") +
        labs(
          title = paste(tissue_name, "-", target, "Prediction"),
          subtitle = paste("R =", round(pooled_r, 3), "RMSE =", round(pooled_rmse, 3)),
        # subtitle = paste("R² =", round(pooled_r2, 3), "RMSE =", round(pooled_rmse, 3)), # use if R² is required
          x = paste("Actual", target),
          y = paste("Predicted", target)
        ) +
        theme_minimal()
      
      ggsave(file.path(output_path, paste0(tissue_name, "_", target, "_scatter.pdf")), 
             scatter_plot, width = 10, height = 6, dpi = 300)
    }
  }
  
  all_results[[target]] <- target_results
  saveRDS(target_results, file.path(results_path, paste0(target, "_model_results.rds")))
}
G3;
Processing target: SMRIN
gG3;
Analyzing Skin for SMRIN
gG3;
Analyzing Adipose for SMRIN
gG3;
Analyzing Nerve for SMRIN
gG3;
Analyzing Muscle for SMRIN
gG3;
Analyzing Artery for SMRIN
gG3;
Analyzing Heart for SMRIN
g
saveRDS(all_results, file.path(results_path, "all_tissue_models_complete.rds"))

#—————————————————————————— # 4. Model Performance Summary #——————————————————————————

# Create performance summary
create_performance_summary <- function(results, target_name) {
  summary_df <- data.frame()
  
  for(tissue in names(results)) {
    tissue_result <- results[[tissue]]
    if(is.null(tissue_result)) next
    
    row <- data.frame(
      Tissue = tissue,
      Samples = tissue_result$sample_size,
      RMSE = round(tissue_result$performance$pooled_rmse, 3),
      R = round(tissue_result$performance$pooled_r, 3),
    # R_squared = round(tissue_result$performance$pooled_r2, 3), # use if R² is required
      MAE = round(tissue_result$performance$pooled_mae, 3),
      Consistent_Features = length(tissue_result$consistent_features)
    )
    summary_df <- rbind(summary_df, row)
  }
  
  summary_df <- summary_df[order(abs(summary_df$R), decreasing = TRUE),]  # Sort by absolute R
  summary_df$Performance_Category <- cut(abs(summary_df$R),  # Use absolute R for categories
                                      breaks = c(-Inf, 0.316, 0.548, 0.707, Inf), 
                                      labels = c("Poor", "Moderate", "Good", "Excellent"))  

# summary_df <- summary_df[order(summary_df$R_squared, decreasing = TRUE),] # Sort by R²
# summary_df$Performance_Category <- cut(summary_df$R_squared, # Use R² for categories
                                  #  breaks = c(-Inf, 0.1, 0.3, 0.5, Inf), 
                                  #  labels = c("Poor", "Moderate", "Good", "Excellent"))
  
  return(summary_df)
}

# Create summaries
rin_summary <- create_performance_summary(all_results[["SMRIN"]], "RIN Score")
autolysis_summary <- create_performance_summary(all_results[["SMATSSCR"]], "Autolysis Score")

# Save summaries
write.csv(rin_summary, file.path(results_path, "rin_model_summary.csv"), row.names = FALSE)
write.csv(autolysis_summary, file.path(results_path, "autolysis_model_summary.csv"), row.names = FALSE)

# Create comparison
model_comparison <- merge(
  rin_summary[, c("Tissue", "R")],
  autolysis_summary[, c("Tissue", "R")],
  by = "Tissue",
  suffixes = c("_RIN", "_Auto")
)
model_comparison$Better_Model <- ifelse(abs(model_comparison$R_RIN) > abs(model_comparison$R_Auto), 
                                      "RIN", "Autolysis")

# Create comparison when if required for R²
# model_comparison <- merge(
#   rin_summary[, c("Tissue", "R_squared")],
#   autolysis_summary[, c("Tissue", "R_squared")],
#   by = "Tissue",
#   suffixes = c("_RIN", "_Auto")
# )
# model_comparison$Better_Model <- ifelse(model_comparison$R_squared_RIN > model_comparison$R_squared_Auto, 
#                                       "RIN", "Autolysis")
write.csv(model_comparison, file.path(results_path, "model_comparison.csv"), row.names = FALSE)

#—————————————————————————— # 5. Figure 3 - Model Performance Visualization #——————————————————————————

# Panel A & B: Performance bar plots
create_horizontal_barplot <- function(all_results, score_type, panel_label) {
  r_values <- sapply(all_results[[score_type]], function(x) {
    if(is.null(x)) return(0)
    return(x$performance$pooled_r)
  # return(sqrt(x$performance$pooled_r2)) # use when required for R²
  })
  
  plot_data <- data.frame(
    Tissue = names(r_values),
    R = r_values
  )[order(-r_values), ]
  
  plot_data$Tissue <- factor(plot_data$Tissue, levels = rev(plot_data$Tissue))
  
  if(score_type == "SMRIN") {
    color_gradient <- colorRampPalette(c("#E8F5E8", "#66cdaa", "#2E8B57"))(100)
  } else {
    color_gradient <- colorRampPalette(c("#FFF2E6", "#FC8D62", "#D94801"))(100)
  }
  
  bar_plot <- ggplot(plot_data, aes(x = R, y = Tissue, fill = R)) +
    geom_bar(stat = "identity", width = 0.8) +
    geom_text(aes(label = sprintf("%.3f", R), x = R/2), 
              hjust = 0.5, size = 3.5, fontface = "bold") +
    scale_fill_gradientn(colors = color_gradient, guide = "none") +
    theme_minimal() +
    labs(title = panel_label, x = "Correlation Coefficient (R)", y = "") +
    theme(
      plot.title = element_text(size = 20, face = "bold", hjust = 0),
      axis.title.x = element_text(size = 14, face = "bold"),
      axis.text.y = element_text(size = 13, face = "bold")
    )
  
  return(bar_plot)
}

rin_barplot <- create_horizontal_barplot(all_results, "SMRIN", "A")
autolysis_barplot <- create_horizontal_barplot(all_results, "SMATSSCR", "B")

# Panel C: RIN scatter plots
create_scatter_panel <- function(all_results, top_tissues) {
  plots <- list()
  
  for(i in 1:3) {
    result <- all_results[["SMRIN"]][[top_tissues[i]]]
    if(is.null(result)) next
    
    all_preds <- result$all_predictions
    r_value <- result$performance$pooled_r
  # r_value <- sqrt(result$performance$pooled_r2) # use when required for R²
    rmse_value <- result$performance$pooled_rmse
    
    p <- ggplot(all_preds, aes(x = actual, y = predicted)) +
      geom_point(alpha = 0.7, color = "#66cdaa", size = 1.5) +
      geom_abline(intercept = 0, slope = 1, color = "red", linetype = "dashed") +
      geom_smooth(method = "lm", se = TRUE, color = "blue") +
      ggtitle(paste0(top_tissues[i], " | R = ", round(r_value, 3))) +
      theme_minimal()
    
    plots[[i]] <- p
  }
  
  return(plots)
}

# Get top 3 tissues
all_r_rin <- sapply(all_results[["SMRIN"]], function(x) {
  if(is.null(x)) return(0)
  return(abs(x$performance$pooled_r))  # Use absolute value for sorting
# return(sqrt(x$performance$pooled_r2)) # use when required for R²
})
top_rin_tissues <- names(sort(all_r_rin, decreasing = TRUE))[1:3]

rin_scatter_plots <- create_scatter_panel(all_results, top_rin_tissues)
rin_grid <- grid.arrange(grobs = rin_scatter_plots, nrow = 1,
                        bottom = textGrob("Actual RIN Score", gp = gpar(fontsize = 18, fontface = "bold")),
                        left = textGrob("Predicted RIN Score", rot = 90, gp = gpar(fontsize = 18, fontface = "bold")),
                        top = textGrob("C", x = 0.02, just = "left", gp = gpar(fontsize = 18, fontface = "bold")))


# Panel D: Autolysis box plots
create_boxplot_panel <- function(all_results, top_tissues) {
  plots <- list()
  
  for(i in 1:3) {
    result <- all_results[["SMATSSCR"]][[top_tissues[i]]]
    if(is.null(result)) next
    
    all_preds <- result$all_predictions
    all_preds$actual_factor <- factor(all_preds$actual, levels = c(0, 1, 2, 3))
    r_value <- sqrt(result$performance$pooled_r2)
    rmse_value <- result$performance$pooled_rmse
    
    p <- ggplot(all_preds, aes(x = actual_factor, y = predicted)) +
      geom_boxplot(aes(fill = actual_factor), outlier.shape = NA) +
      geom_jitter(width = 0.2, height = 0, alpha = 0.2, color = "#21908d") +
      scale_fill_manual(values = c("0" = "#e0e0e0", "1" = "#bdd7e7", 
                                 "2" = "#9ecae1", "3" = "#6baed6")) +
      ggtitle(paste0(top_tissues[i], " | R = ", round(r_value, 3))) +
      theme_minimal() +
      theme(legend.position = "none")
    
    plots[[i]] <- p
  }
  
  return(plots)
}

all_r_auto <- sapply(all_results[["SMATSSCR"]], function(x) {
  if(is.null(x)) return(0)
  return(sqrt(x$performance$pooled_r2))
})
top_auto_tissues <- names(sort(all_r_auto, decreasing = TRUE))[1:3]

auto_boxplot_plots <- create_boxplot_panel(all_results, top_auto_tissues)
autolysis_grid <- grid.arrange(grobs = auto_boxplot_plots, nrow = 1,
                              bottom = textGrob("Actual Category", gp = gpar(fontsize = 18, fontface = "bold")),
                              left = textGrob("Predicted Score", rot = 90, gp = gpar(fontsize = 18, fontface = "bold")),
                              top = textGrob("D", x = 0.02, just = "left", gp = gpar(fontsize = 18, fontface = "bold")))


# Combine all panels
combined_barplots <- grid.arrange(rin_barplot, autolysis_barplot, ncol = 2)

combined_CD_plots <- grid.arrange(rin_grid, autolysis_grid, nrow = 2)

figure3_final <- grid.arrange(combined_barplots, combined_CD_plots, ncol = 2, widths = c(0.45, 0.55))


ggsave(file.path(output_path, "Figure3_Final_Complete.pdf"), 
       figure3_final, width = 20, height = 12, dpi = 600, bg = "white")

#—————————————————————————— # 6. Figure 4 - Feature Importance #——————————————————————————

# Extract top features
extract_features <- function(results, target, top_n = 20) {
  all_tissue_features <- data.frame()
  
  for(tissue in names(results[[target]])) {
    tissue_result <- results[[target]][[tissue]]
    if(is.null(tissue_result)) next
    
    if(!is.null(tissue_result$feature_counts)) {
      features <- names(tissue_result$feature_counts)
      counts <- as.numeric(tissue_result$feature_counts)
      
      tissue_df <- data.frame(
        feature = features,
        count = counts,
        tissue = tissue,
        stringsAsFactors = FALSE
      )
      all_tissue_features <- rbind(all_tissue_features, tissue_df)
    }
  }
  
  feature_summary <- all_tissue_features %>%
    group_by(feature) %>%
    summarize(total_count = sum(count)) %>%
    arrange(desc(total_count)) %>%
    slice(1:top_n)
  
  feature_matrix <- all_tissue_features %>%
    filter(feature %in% feature_summary$feature) %>%
    reshape2::dcast(feature ~ tissue, value.var = "count", fill = 0)
  
  return(list(summary = feature_summary, matrix = feature_matrix))
}

rin_features <- extract_features(all_results, "SMRIN")
auto_features <- extract_features(all_results, "SMATSSCR")

# Find common features
common_features <- intersect(rin_features$summary$feature, auto_features$summary$feature)
rin_only_features <- setdiff(rin_features$summary$feature, auto_features$summary$feature)
auto_only_features <- setdiff(auto_features$summary$feature, rin_features$summary$feature)

# Create heatmaps
create_heatmap <- function(feature_data, color_palette) {
  feature_long <- reshape2::melt(feature_data$matrix, id.vars = "feature",
                               variable.name = "tissue", value.name = "count")
  
  feature_long <- merge(feature_long, feature_data$summary[, c("feature", "total_count")], by = "feature")
  feature_long$feature_label <- sub("feature_", "", feature_long$feature)
  
  p <- ggplot(feature_long, aes(x = tissue, y = reorder(feature_label, total_count))) +
    geom_tile(aes(fill = count), color = "white", size = 0.1) +
    scale_fill_gradientn(colors = color_palette, name = "Selection\nFrequency") +
    labs(x = NULL, y = "Feature ID") +
    theme_minimal() +
    theme(
      axis.text.x = element_text(angle = 65, hjust = 1, size = 12, face = "bold"),
      axis.text.y = element_text(size = 12, face = "bold"),
      panel.grid = element_blank()
    )
  
  return(p)
}

rin_heatmap <- create_heatmap(rin_features, 
                             colorRampPalette(c("white", "#66CDAA", "#006400"))(100)) +
  ggtitle("A")

auto_heatmap <- create_heatmap(auto_features,
                              colorRampPalette(c("white", "#FC8D62", "#D94801"))(100)) +
  ggtitle("B")

# Create Venn diagram
venn_plot <- ggplot() +
  ggforce::geom_circle(data = data.frame(x = c(-1, 1), y = c(0, 0), r = c(2, 2),
                                        category = c("RIN Features", "Autolysis Features")),
                      aes(x0 = x, y0 = y, r = r, fill = category),
                      alpha = 0.5, color = "black", size = 0.8) +
  geom_text(data = data.frame(x = c(-1.65, 0, 1.65), y = c(0, 0, 0),
                              label = c(length(rin_only_features), 
                                       length(common_features),
                                       length(auto_only_features))),
           aes(x = x, y = y, label = label), size = 10, fontface = "bold") +
  scale_fill_manual(values = c("RIN Features" = "#66CDAA", "Autolysis Features" = "#FC8D62")) +
  theme_void() +
  theme(legend.position = "none") +
  coord_fixed() +
  xlim(-3, 3) + ylim(-2, 2) +
  ggtitle("C")

# Combine plots
heatmaps <- plot_grid(rin_heatmap, auto_heatmap, ncol = 2)
figure4 <- plot_grid(heatmaps, venn_plot, nrow = 2, rel_heights = c(2, 1.5))

ggsave(file.path(output_path, "Figure4_Feature.pdf"), 
       figure4, width = 14, height = 12, dpi = 600, bg = "transparent")

#—————————————————————————— # 7. Additional Visualizations and Comparative Analysis #——————————————————————————

# Create comparative performance chart for top tissues
create_comparative_bar_chart <- function(rin_results, autolysis_results) {
  common_tissues <- intersect(rin_results$Tissue, autolysis_results$Tissue)
  
  comparison_data <- data.frame(
    Tissue = common_tissues,
    RIN_R = rin_results$R[match(common_tissues, rin_results$Tissue)],
    Autolysis_R = autolysis_results$R[match(common_tissues, autolysis_results$Tissue)]
  )
  
  comparison_data$Average_R <- (abs(comparison_data$RIN_R) + abs(comparison_data$Autolysis_R)) / 2
  comparison_data <- comparison_data[order(-comparison_data$Average_R), ]
  top_tissues <- head(comparison_data, 15)
  
  plot_data <- reshape2::melt(top_tissues[, c("Tissue", "RIN_R", "Autolysis_R")], 
                             id.vars = "Tissue", variable.name = "Model", value.name = "R_value")
  plot_data$Tissue <- factor(plot_data$Tissue, levels = top_tissues$Tissue)
  
  comparison_plot <- ggplot(plot_data, aes(x = Tissue, y = R_value, fill = Model)) +
    geom_bar(stat = "identity", position = "dodge", alpha = 0.8) +
    geom_text(aes(label = sprintf("%.3f", R_value)), 
             position = position_dodge(width = 0.9), vjust = -0.3, size = 3) +
    scale_fill_manual(values = c("RIN_R" = "#66CDAA", "Autolysis_R" = "#FC8D62"),
                     labels = c("RIN", "Autolysis")) +
    theme_minimal() +
    theme(
      axis.text.x = element_text(angle = 45, hjust = 1, size = 11, face = "bold"),
      plot.title = element_text(size = 16, face = "bold"),
      legend.position = "top"
    ) +
    labs(
      title = "Model Performance Comparison Across Top Tissues",
      subtitle = "Top 15 tissues ranked by average correlation coefficient",
      x = "Tissue Type",
      y = "Correlation Coefficient (R)",
      fill = "Model Type"
    )
  
  ggsave(file.path(output_path, "comparative_performance_chart.pdf"), 
         comparison_plot, width = 14, height = 8, dpi = 300)
  
  return(comparison_plot)
}

# Create correlation distribution analysis
create_correlation_distribution <- function(results_df, target_name) {
  r_dist_plot <- ggplot(results_df, aes(x = abs(R))) +
    geom_histogram(binwidth = 0.05, fill = "steelblue", color = "white", alpha = 0.8) +
    geom_vline(aes(xintercept = mean(abs(R))), 
              color = "red", linetype = "dashed", size = 1.2) +
    geom_vline(aes(xintercept = median(abs(R))), 
              color = "darkgreen", linetype = "dashed", size = 1.2) +
    annotate("text", x = mean(abs(results_df$R)) + 0.08, y = max(table(cut(abs(results_df$R), 20))) * 0.8, 
             label = paste("Mean =", round(mean(abs(results_df$R)), 3)),
             color = "red", fontface = "bold") +
    annotate("text", x = median(abs(results_df$R)) - 0.08, y = max(table(cut(abs(results_df$R), 20))) * 0.6, 
             label = paste("Median =", round(median(abs(results_df$R)), 3)),
             color = "darkgreen", fontface = "bold") +
    theme_minimal() +
    labs(
      title = paste("Distribution of Correlation Coefficients:", target_name),
      subtitle = paste("Analysis of", nrow(results_df), "tissue-specific models"),
      x = "Absolute Correlation Coefficient |R|",
      y = "Frequency"
    ) +
    theme(
      plot.title = element_text(size = 14, face = "bold"),
      axis.title = element_text(size = 12, face = "bold")
    )
  
  ggsave(file.path(output_path, paste0(gsub(" ", "_", tolower(target_name)), "_correlation_distribution.pdf")), 
         r_dist_plot, width = 10, height = 7, dpi = 300)
  
  return(r_dist_plot)
}

# Create enhanced scatter plots with residual analysis
create_enhanced_scatter_plots <- function(results, target_name, top_n = 6) {
  all_r_values <- sapply(results, function(x) {
    if(is.null(x)) return(0)
    return(abs(x$performance$pooled_r))
  })
  
  top_tissues <- names(sort(all_r_values, decreasing = TRUE))[1:min(top_n, length(all_r_values))]
  
  for(tissue in top_tissues) {
    if(is.null(results[[tissue]])) next
    
    predictions <- results[[tissue]]$all_predictions
    r_value <- results[[tissue]]$performance$pooled_r
    rmse_value <- results[[tissue]]$performance$pooled_rmse
    
    predictions$residual <- predictions$predicted - predictions$actual
    
    scatter_plot <- ggplot(predictions, aes(x = actual, y = predicted)) +
      geom_point(alpha = 0.7, color = "#2E86AB", size = 2.5) +
      geom_abline(intercept = 0, slope = 1, color = "red", linetype = "dashed", size = 1) +
      geom_smooth(method = "lm", se = TRUE, color = "darkblue", alpha = 0.3) +
      labs(
        title = paste(tissue, "-", target_name, "Prediction Model"),
        subtitle = paste("R =", round(r_value, 3), "| RMSE =", round(rmse_value, 3)),
        x = paste("Actual", target_name),
        y = paste("Predicted", target_name)
      ) +
      theme_minimal() +
      theme(
        plot.title = element_text(size = 14, face = "bold"),
        plot.subtitle = element_text(size = 12),
        axis.title = element_text(size = 11, face = "bold")
      )
    
    ggsave(file.path(output_path, paste0("enhanced_", tissue, "_", gsub(" ", "_", tolower(target_name)), "_scatter.pdf")), 
           scatter_plot, width = 10, height = 8, dpi = 300)
  }
}

# Execute comparative visualizations
comparison_chart <- create_comparative_bar_chart(rin_summary, autolysis_summary)
rin_distribution <- create_correlation_distribution(rin_summary, "RIN Score")
autolysis_distribution <- create_correlation_distribution(autolysis_summary, "Autolysis Score")

create_enhanced_scatter_plots(all_results[["SMRIN"]], "RIN Score", top_n = 6)
create_enhanced_scatter_plots(all_results[["SMATSSCR"]], "Autolysis Score", top_n = 6)

#—————————————————————————— # 8. Figure 5: Model Performance vs Sample Size Analysis #——————————————————————————

create_sample_size_analysis <- function(rin_summary, autolysis_summary) {
  
  combined_data <- rbind(
    data.frame(
      Tissue = rin_summary$Tissue,
      Samples = rin_summary$Samples, 
      R_value = abs(rin_summary$R),
      Model = "RIN"
    ),
    data.frame(
      Tissue = autolysis_summary$Tissue,
      Samples = autolysis_summary$Samples,
      R_value = abs(autolysis_summary$R),
      Model = "Autolysis"
    )
  )
  
  scatter_panel <- ggplot(combined_data, aes(x = Samples, y = R_value, color = Model)) +
    geom_point(size = 3.5, alpha = 0.8) +
    geom_smooth(method = "lm", se = TRUE, alpha = 0.3, size = 1.2) +
    scale_color_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
    theme_minimal() +
    labs(
      title = "A",
      x = "Sample Size (n)",
      y = "Correlation Coefficient |R|",
      subtitle = "Relationship between sample size and model performance"
    ) +
    theme(
      plot.title = element_text(size = 20, face = "bold", hjust = 0),
      plot.subtitle = element_text(size = 12),
      legend.position = "top",
      legend.title = element_text(size = 12, face = "bold"),
      axis.title = element_text(size = 12, face = "bold")
    )
  
  combined_data$size_category <- cut(combined_data$Samples, 
                                   breaks = c(0, 50, 100, 200, 500, Inf),
                                   labels = c("≤50", "51-100", "101-200", "201-500", ">500"))
  
  bin_summary <- combined_data %>%
    group_by(size_category, Model) %>%
    summarise(
      mean_r = mean(R_value, na.rm = TRUE),
      se_r = sd(R_value, na.rm = TRUE) / sqrt(n()),
      tissue_count = n(),
      .groups = 'drop'
    )
  
  bin_panel <- ggplot(bin_summary, aes(x = size_category, y = mean_r, fill = Model)) +
    geom_bar(stat = "identity", position = "dodge", alpha = 0.85, width = 0.7) +
    geom_errorbar(aes(ymin = mean_r - se_r, ymax = mean_r + se_r),
                  position = position_dodge(width = 0.7), width = 0.25, size = 0.8) +
    geom_text(aes(label = tissue_count), 
              position = position_dodge(width = 0.7), 
              vjust = -1.5, size = 3.5, fontface = "bold") +
    scale_fill_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
    theme_minimal() +
    labs(
      title = "B",
      x = "Sample Size Categories",
      y = "Mean |R| ± SE",
      subtitle = "Performance stratified by sample size (numbers show tissue count)"
    ) +
    theme(
      plot.title = element_text(size = 20, face = "bold", hjust = 0),
      plot.subtitle = element_text(size = 12),
      legend.position = "none",
      axis.title = element_text(size = 12, face = "bold")
    )
  
  figure5_combined <- grid.arrange(scatter_panel, bin_panel, ncol = 2, widths = c(1, 1))
  
  return(figure5_combined)
}

analyze_sample_size_effects <- function(combined_data) {
  rin_data <- combined_data[combined_data$Model == "RIN", ]
  auto_data <- combined_data[combined_data$Model == "Autolysis", ]
  
  rin_cor <- cor.test(rin_data$Samples, rin_data$R_value)
  auto_cor <- cor.test(auto_data$Samples, auto_data$R_value)
  
  return(list(rin_test = rin_cor, autolysis_test = auto_cor))
}

figure5_plot <- create_sample_size_analysis(rin_summary, autolysis_summary)

sample_size_stats <- analyze_sample_size_effects(rbind(
  data.frame(Tissue = rin_summary$Tissue, Samples = rin_summary$Samples, 
             R_value = abs(rin_summary$R), Model = "RIN"),
  data.frame(Tissue = autolysis_summary$Tissue, Samples = autolysis_summary$Samples,
             R_value = abs(autolysis_summary$R), Model = "Autolysis")
))

ggsave(file.path(output_path, "Figure5_Sample_Size_Analysis.pdf"), 
       figure5_plot, width = 16, height = 8, dpi = 600, bg = "white")
G2;H2;Warningh in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  for '≤50' in 'mbcsToSbcs': <= substituted for ≤ (U+2264)g

#—————————————————————————— # 9. Top 5 Tissues Quality Overlay #——————————————————————————

# Get top 5 tissues for each score
get_top5_tissues <- function(target_name) {
  r_values <- sapply(all_results[[target_name]], function(x) {
    if(is.null(x) || is.null(x$performance$pooled_r2)) return(0)
  # return(sqrt(x$performance$pooled_r2)) # use when required for R²
    return(abs(x$performance$pooled_r))  # Use absolute R for ranking
  })
  
  r_values <- r_values[r_values > 0]
  top5 <- sort(r_values, decreasing = TRUE)[1:5]
  
  return(data.frame(
    tissue = names(top5),
    correlation_r = as.numeric(top5)
  ))
}

top5_rin <- get_top5_tissues("SMRIN")
top5_auto <- get_top5_tissues("SMATSSCR")

# Create overlay plots
create_simple_overlay <- function(tissue_name, score_type, color_palette) {
  tissue_data <- merged_gtex[tissue_main == tissue_name]
  
  if(nrow(tissue_data) < 10) {
    return(ggplot() + theme_void())
  }
  
  # Get features and run UMAP
  feature_cols <- grep("^feature_", names(tissue_data), value = TRUE)
  feature_matrix <- as.matrix(tissue_data[, ..feature_cols])
  feature_matrix <- scale(feature_matrix[, apply(feature_matrix, 2, var) > 1e-10])
  
  set.seed(123)
  umap_result <- umap(feature_matrix, n_neighbors = min(15, nrow(feature_matrix) - 1))
  
  plot_data <- data.frame(
    UMAP1 = umap_result[, 1],
    UMAP2 = umap_result[, 2],
    Score = tissue_data[[score_type]]
  )
  
  # Separate data with and without scores
  data_with_scores <- plot_data[!is.na(plot_data$Score), ]
  data_missing_scores <- plot_data[is.na(plot_data$Score), ]
  
  # Get R value
  tissue_result <- all_results[[score_type]][[tissue_name]]
  r_value <- if(!is.null(tissue_result)) tissue_result$performance$pooled_r else 0
# r_value <- if(!is.null(tissue_result)) sqrt(tissue_result$performance$pooled_r2) else 0 # use when required for R²
  
  p <- ggplot() +
    geom_point(data = data_missing_scores, aes(x = UMAP1, y = UMAP2), 
               color = "lightgrey", alpha = 0.5, size = 1.5) +
    geom_point(data = data_with_scores, aes(x = UMAP1, y = UMAP2, color = Score), 
               alpha = 0.7, size = 2) +
    color_palette +
    theme_minimal() +
    labs(
      title = tissue_name,
      subtitle = paste("R =", sprintf("%.3f", r_value)),
      x = "UMAP1", y = "UMAP2"
    )
  
  return(p)
}

# Define color palettes
rin_palette <- scale_color_viridis_c(name = "RIN\nScore", option = "viridis")
auto_palette <- scale_color_viridis_c(name = "Autolysis\nScore", option = "plasma")

# Create plots
rin_plots <- lapply(1:5, function(i) {
  create_simple_overlay(top5_rin$tissue[i], "SMRIN", rin_palette)
})

auto_plots <- lapply(1:5, function(i) {
  create_simple_overlay(top5_auto$tissue[i], "SMATSSCR", auto_palette)
})

# Combine panels
panel_a <- grid.arrange(grobs = rin_plots, nrow = 1,
                       top = textGrob("A", gp = gpar(fontsize = 20, fontface = "bold"),
                                     x = 0.02, just = "left"))


panel_b <- grid.arrange(grobs = auto_plots, nrow = 1,
                       top = textGrob("B", gp = gpar(fontsize = 20, fontface = "bold"),
                                     x = 0.02, just = "left"))


final_plot <- grid.arrange(panel_a, panel_b, nrow = 2)


ggsave(file.path(output_path, "Top5_Tissues_Quality_Overlay_AB.pdf"),
       final_plot, width = 20, height = 12, dpi = 300, bg = "white")

#—————————————————————————— # 10. Figure 6: Cross-Tissue Feature Consistency Analysis #——————————————————————————

create_feature_consistency_analysis <- function(all_results) {
  
  extract_consistent_features <- function(target_results, min_selections = 3) {
    tissue_features <- list()
    
    for(tissue in names(target_results)) {
      if(!is.null(target_results[[tissue]]$feature_counts)) {
        features <- names(target_results[[tissue]]$feature_counts)
        counts <- as.numeric(target_results[[tissue]]$feature_counts)
        selected_features <- features[counts >= min_selections]
        tissue_features[[tissue]] <- selected_features
      }
    }
    
    return(tissue_features)
  }
  
  rin_tissue_features <- extract_consistent_features(all_results[["SMRIN"]])
  auto_tissue_features <- extract_consistent_features(all_results[["SMATSSCR"]])
  
  create_consistency_heatmap <- function(tissue_features, title, color_scheme) {
    all_features <- unique(unlist(tissue_features))
    all_tissues <- names(tissue_features)
    
    selection_matrix <- matrix(0, nrow = length(all_features), ncol = length(all_tissues))
    rownames(selection_matrix) <- all_features
    colnames(selection_matrix) <- all_tissues
    
    for(tissue in all_tissues) {
      selected <- tissue_features[[tissue]]
      selection_matrix[selected, tissue] <- 1
    }
    
    consistency_scores <- rowSums(selection_matrix) / ncol(selection_matrix)
    top_features <- names(sort(consistency_scores, decreasing = TRUE))[1:25]
    plot_matrix <- selection_matrix[top_features, ]
    
    plot_data <- expand.grid(Feature = rownames(plot_matrix), 
                           Tissue = colnames(plot_matrix))
    plot_data$Selected <- as.vector(plot_matrix)
    plot_data$Consistency_Score <- consistency_scores[plot_data$Feature]
    plot_data$Feature_ID <- sub("feature_", "", plot_data$Feature)
    
    heatmap_plot <- ggplot(plot_data, aes(x = Tissue, y = reorder(Feature_ID, Consistency_Score))) +
      geom_tile(aes(fill = factor(Selected)), color = "white", size = 0.3) +
      scale_fill_manual(values = c("0" = "white", "1" = color_scheme), 
                       name = "Selected", labels = c("No", "Yes")) +
      theme_minimal() +
      theme(
        axis.text.x = element_text(angle = 45, hjust = 1, size = 9, face = "bold"),
        axis.text.y = element_text(size = 9),
        panel.grid = element_blank(),
        legend.position = "right",
        plot.title = element_text(size = 16, face = "bold")
      ) +
      labs(
        title = title,
        x = NULL,
        y = "Feature ID"
      )
    
    return(heatmap_plot)
  }
  
  panel_a <- create_consistency_heatmap(rin_tissue_features, "A. RIN Model Feature Consistency", "#2E8B57")
  panel_b <- create_consistency_heatmap(auto_tissue_features, "B. Autolysis Model Feature Consistency", "#D2691E")
  
  calculate_stability_distribution <- function(tissue_features) {
    all_features <- unique(unlist(tissue_features))
    stability_scores <- sapply(all_features, function(feature) {
      tissues_with_feature <- sum(sapply(tissue_features, function(x) feature %in% x))
      return(tissues_with_feature / length(tissue_features))
    })
    return(stability_scores)
  }
  
  rin_stability <- calculate_stability_distribution(rin_tissue_features)
  auto_stability <- calculate_stability_distribution(auto_tissue_features)
  
  stability_comparison <- data.frame(
    Feature = c(names(rin_stability), names(auto_stability)),
    Stability = c(rin_stability, auto_stability),
    Model = rep(c("RIN", "Autolysis"), c(length(rin_stability), length(auto_stability)))
  )
  
  panel_c <- ggplot(stability_comparison, aes(x = Stability, fill = Model)) +
    geom_histogram(alpha = 0.75, bins = 15, position = "identity") +
    geom_vline(data = stability_comparison %>% group_by(Model) %>% summarise(mean_stab = mean(Stability)),
               aes(xintercept = mean_stab, color = Model), linetype = "dashed", size = 1.2) +
    scale_fill_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
    scale_color_manual(values = c("RIN" = "#2E8B57", "Autolysis" = "#D2691E")) +
    theme_minimal() +
    labs(
      title = "C. Feature Stability Distribution",
      x = "Stability Score (Fraction of Tissues)",
      y = "Number of Features",
      subtitle = "Distribution of cross-tissue feature consistency"
    ) +
    theme(
      plot.title = element_text(size = 16, face = "bold"),
      plot.subtitle = element_text(size = 12),
      legend.position = "top",
      axis.title = element_text(size = 11, face = "bold")
    )
  
  top_panels <- grid.arrange(panel_a, panel_b, ncol = 2)
  figure6_combined <- grid.arrange(top_panels, panel_c, nrow = 2, heights = c(2, 1))
  
  return(figure6_combined)
}

calculate_feature_overlap_stats <- function(all_results) {
  extract_top_features <- function(target_results, n = 50) {
    feature_counts <- list()
    for(tissue in names(target_results)) {
      if(!is.null(target_results[[tissue]]$feature_counts)) {
        counts <- target_results[[tissue]]$feature_counts
        for(feat in names(counts)) {
          if(is.null(feature_counts[[feat]])) feature_counts[[feat]] <- 0
          feature_counts[[feat]] <- feature_counts[[feat]] + counts[[feat]]
        }
      }
    }
    
    sorted_features <- sort(unlist(feature_counts), decreasing = TRUE)
    return(names(sorted_features)[1:min(n, length(sorted_features))])
  }
  
  rin_top <- extract_top_features(all_results[["SMRIN"]])
  auto_top <- extract_top_features(all_results[["SMATSSCR"]])
  
  overlap_count <- length(intersect(rin_top, auto_top))
  rin_unique <- length(setdiff(rin_top, auto_top))
  auto_unique <- length(setdiff(auto_top, rin_top))
  
  return(list(common = overlap_count, rin_unique = rin_unique, auto_unique = auto_unique))
}

figure6_plot <- create_feature_consistency_analysis(all_results)

overlap_stats <- calculate_feature_overlap_stats(all_results)

ggsave(file.path(output_path, "Figure6_Feature_Consistency.pdf"), 
       figure6_plot, width = 18, height = 14, dpi = 600, bg = "white")

#—————————————————————————— # 11. Figure 7: Model Residual Analysis #——————————————————————————

create_comprehensive_residual_analysis <- function(all_results, top_n = 6) {
  
  identify_top_tissues <- function(target_results, n) {
    performance_scores <- sapply(target_results, function(x) {
      if(is.null(x)) return(0)
      return(abs(x$performance$pooled_r))
    })
    top_tissues <- names(sort(performance_scores, decreasing = TRUE))[1:n]
    return(top_tissues[!is.na(top_tissues)])
  }
  
  top_rin_tissues <- identify_top_tissues(all_results[["SMRIN"]], top_n)
  top_auto_tissues <- identify_top_tissues(all_results[["SMATSSCR"]], top_n)
  
  create_residual_panel <- function(tissues, target, color, panel_title) {
    residual_plots <- list()
    
    for(i in 1:min(3, length(tissues))) {
      tissue_name <- tissues[i]
      result <- all_results[[target]][[tissue_name]]
      
      if(!is.null(result) && !is.null(result$all_predictions)) {
        predictions <- result$all_predictions
        predictions$residual <- predictions$predicted - predictions$actual
        r_value <- result$performance$pooled_r
        
        residual_plot <- ggplot(predictions, aes(x = actual, y = residual)) +
          geom_point(alpha = 0.7, color = color, size = 2) +
          geom_hline(yintercept = 0, linetype = "dashed", color = "red", size = 1) +
          geom_smooth(method = "loess", se = TRUE, color = "darkblue", alpha = 0.3) +
          theme_minimal() +
          labs(
            title = paste(tissue_name, "(R =", round(r_value, 3), ")"),
            x = if(i == 3) paste("Actual", ifelse(target == "SMRIN", "RIN", "Autolysis")) else "",
            y = if(i == 1) "Residual" else ""
          ) +
          theme(
            plot.title = element_text(size = 12, face = "bold"),
            axis.title = element_text(size = 10, face = "bold")
          )
        
        residual_plots[[i]] <- residual_plot
      }
    }
    
    panel <- grid.arrange(grobs = residual_plots, nrow = 1,
                         top = textGrob(panel_title, gp = gpar(fontsize = 16, fontface = "bold")))
    return(panel)
  }
  
  panel_a <- create_residual_panel(top_rin_tissues, "SMRIN", "#66CDAA", "A. RIN Model Residuals")
  panel_b <- create_residual_panel(top_auto_tissues, "SMATSSCR", "#FC8D62", "B. Autolysis Model Residuals")
  
  create_residual_distribution <- function() {
    all_residuals <- data.frame()
    
    for(target in c("SMRIN", "SMATSSCR")) {
      for(tissue in names(all_results[[target]])) {
        result <- all_results[[target]][[tissue]]
        if(!is.null(result) && !is.null(result$all_predictions)) {
          predictions <- result$all_predictions
          residual_data <- data.frame(
            tissue = tissue,
            target = ifelse(target == "SMRIN", "RIN", "Autolysis"),
            residual = predictions$predicted - predictions$actual,
            abs_residual = abs(predictions$predicted - predictions$actual)
          )
          all_residuals <- rbind(all_residuals, residual_data)
        }
      }
    }
    
    distribution_plot <- ggplot(all_residuals, aes(x = abs_residual, fill = target)) +
      geom_histogram(alpha = 0.75, bins = 30, position = "identity") +
      geom_vline(data = all_residuals %>% group_by(target) %>% summarise(mean_res = mean(abs_residual)),
                 aes(xintercept = mean_res, color = target), linetype = "dashed", size = 1.2) +
      scale_fill_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
      scale_color_manual(values = c("RIN" = "#2E8B57", "Autolysis" = "#D2691E")) +
      theme_minimal() +
      labs(
        title = "C. Distribution of Absolute Residuals",
        x = "Absolute Residual Magnitude",
        y = "Frequency",
        fill = "Model Type",
        subtitle = "Comparison of prediction errors across all tissue models"
      ) +
      theme(
        plot.title = element_text(size = 16, face = "bold"),
        plot.subtitle = element_text(size = 12),
        legend.position = "top",
        axis.title = element_text(size = 11, face = "bold")
      )
    
    return(distribution_plot)
  }
  
  create_performance_residual_relationship <- function() {
    performance_residual_data <- data.frame()
    
    for(target in c("SMRIN", "SMATSSCR")) {
      for(tissue in names(all_results[[target]])) {
        result <- all_results[[target]][[tissue]]
        if(!is.null(result) && !is.null(result$all_predictions)) {
          predictions <- result$all_predictions
          
          perf_data <- data.frame(
            tissue = tissue,
            target = ifelse(target == "SMRIN", "RIN", "Autolysis"),
            r_value = abs(result$performance$pooled_r),
            rmse = result$performance$pooled_rmse,
            mean_abs_residual = mean(abs(predictions$predicted - predictions$actual))
          )
          performance_residual_data <- rbind(performance_residual_data, perf_data)
        }
      }
    }
    
    relationship_plot <- ggplot(performance_residual_data, aes(x = r_value, y = mean_abs_residual, color = target)) +
      geom_point(size = 3.5, alpha = 0.8) +
      geom_smooth(method = "lm", se = TRUE, alpha = 0.3, size = 1.2) +
      scale_color_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
      theme_minimal() +
      labs(
        title = "D. Model Performance vs Residual Magnitude",
        x = "Correlation Coefficient |R|",
        y = "Mean Absolute Residual",
        color = "Model Type",
        subtitle = "Relationship between prediction accuracy and error magnitude"
      ) +
      theme(
        plot.title = element_text(size = 16, face = "bold"),
        plot.subtitle = element_text(size = 12),
        legend.position = "none",
        axis.title = element_text(size = 11, face = "bold")
      )
    
    return(relationship_plot)
  }
  
  panel_c <- create_residual_distribution()
  panel_d <- create_performance_residual_relationship()
  
  bottom_panels <- grid.arrange(panel_c, panel_d, ncol = 2)
  figure7_combined <- grid.arrange(panel_a, panel_b, bottom_panels, nrow = 3, heights = c(1, 1, 1))
  
  return(figure7_combined)
}

analyze_residual_patterns <- function(all_results) {
  residual_stats <- data.frame()
  
  for(target in c("SMRIN", "SMATSSCR")) {
    for(tissue in names(all_results[[target]])) {
      result <- all_results[[target]][[tissue]]
      if(!is.null(result) && !is.null(result$all_predictions)) {
        predictions <- result$all_predictions
        residuals <- predictions$predicted - predictions$actual
        
        stats_row <- data.frame(
          tissue = tissue,
          target = target,
          mean_residual = mean(residuals),
          sd_residual = sd(residuals),
          skewness = e1071::skewness(residuals),
          normality_p = shapiro.test(sample(residuals, min(5000, length(residuals))))$p.value
        )
        residual_stats <- rbind(residual_stats, stats_row)
      }
    }
  }
  
  return(residual_stats)
}

figure7_plot <- create_comprehensive_residual_analysis(all_results)
residual_statistics <- analyze_residual_patterns(all_results)

ggsave(file.path(output_path, "Figure7_Residual_Analysis.pdf"), 
       figure7_plot, width = 16, height = 14, dpi = 600, bg = "white")

#—————————————————————————— # 11. Figure 8: Biological Feature Interpretation #——————————————————————————

create_biological_interpretation_analysis <- function(all_results, merged_gtex) {
  
  analyze_feature_categories <- function() {
    extract_important_features <- function(target_results, n = 50) {
      feature_importance <- c()
      for(tissue in names(target_results)) {
        if(!is.null(target_results[[tissue]]$feature_counts)) {
          features <- names(target_results[[tissue]]$feature_counts)
          counts <- as.numeric(target_results[[tissue]]$feature_counts)
          feature_importance <- c(feature_importance, rep(features, counts))
        }
      }
      
      importance_table <- sort(table(feature_importance), decreasing = TRUE)
      return(names(importance_table)[1:min(n, length(importance_table))])
    }
    
    rin_important <- extract_important_features(all_results[["SMRIN"]])
    auto_important <- extract_important_features(all_results[["SMATSSCR"]])
    
    categorize_features <- function(feature_names) {
      feature_numbers <- as.numeric(sub("feature_", "", feature_names))
      categories <- case_when(
        feature_numbers <= 1024 ~ "Mean",
        feature_numbers <= 2048 ~ "Standard Deviation",
        feature_numbers <= 3072 ~ "Minimum",
        TRUE ~ "Maximum"
      )
      return(categories)
    }
    
    rin_categories <- categorize_features(rin_important)
    auto_categories <- categorize_features(auto_important)
    
    category_summary <- data.frame(
      Category = rep(c("Mean", "Standard Deviation", "Minimum", "Maximum"), 2),
      Count = c(table(rin_categories)[c("Mean", "Standard Deviation", "Minimum", "Maximum")],
               table(auto_categories)[c("Mean", "Standard Deviation", "Minimum", "Maximum")]),
      Model = rep(c("RIN", "Autolysis"), each = 4)
    )
    
    category_summary$Count[is.na(category_summary$Count)] <- 0
    
    category_plot <- ggplot(category_summary, aes(x = Category, y = Count, fill = Model)) +
      geom_bar(stat = "identity", position = "dodge", alpha = 0.85, width = 0.7) +
      geom_text(aes(label = Count), position = position_dodge(width = 0.7), 
                vjust = -0.3, size = 4, fontface = "bold") +
      scale_fill_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
      theme_minimal() +
      labs(
        title = "A. Feature Category Distribution",
        x = "Statistical Property Category",
        y = "Number of Important Features",
        subtitle = "Distribution of key features across statistical measures"
      ) +
      theme(
        plot.title = element_text(size = 16, face = "bold"),
        plot.subtitle = element_text(size = 12),
        legend.position = "top",
        axis.title = element_text(size = 12, face = "bold"),
        axis.text.x = element_text(size = 11, face = "bold")
      )
    
    return(category_plot)
  }
  
  create_tissue_variability_analysis <- function() {
    feature_cols <- grep("^feature_", names(merged_gtex), value = TRUE)
    
    get_feature_importance <- function(target_results) {
      feature_scores <- list()
      for(tissue in names(target_results)) {
        if(!is.null(target_results[[tissue]]$feature_counts)) {
          counts <- target_results[[tissue]]$feature_counts
          for(feat in names(counts)) {
            if(is.null(feature_scores[[feat]])) feature_scores[[feat]] <- 0
            feature_scores[[feat]] <- feature_scores[[feat]] + counts[[feat]]
          }
        }
      }
      
      sorted_scores <- sort(unlist(feature_scores), decreasing = TRUE)
      return(names(sorted_scores)[1:min(25, length(sorted_scores))])
    }
    
    important_features <- get_feature_importance(all_results[["SMRIN"]])
    variability_data <- data.frame()
    
    for(feature in important_features) {
      if(feature %in% feature_cols) {
        tissue_statistics <- merged_gtex[, .(
          mean_value = mean(get(feature), na.rm = TRUE),
          sd_value = sd(get(feature), na.rm = TRUE)
        ), by = tissue_main]
        
        overall_variance <- var(tissue_statistics$mean_value, na.rm = TRUE)
        
        importance_score <- sum(sapply(all_results[["SMRIN"]], function(x) {
          if(!is.null(x$feature_counts) && feature %in% names(x$feature_counts)) {
            return(x$feature_counts[[feature]])
          }
          return(0)
        }))
        
        feature_num <- as.numeric(sub("feature_", "", feature))
        category <- case_when(
          feature_num <= 1024 ~ "Mean",
          feature_num <= 2048 ~ "Std Dev",
          feature_num <= 3072 ~ "Minimum",
          TRUE ~ "Maximum"
        )
        
        variability_data <- rbind(variability_data, data.frame(
          feature = feature,
          tissue_variance = overall_variance,
          importance_score = importance_score,
          category = category
        ))
      }
    }
    
    variability_plot <- ggplot(variability_data, aes(x = tissue_variance, y = importance_score, color = category)) +
      geom_point(size = 4, alpha = 0.8) +
      geom_smooth(method = "lm", se = TRUE, alpha = 0.3, color = "darkblue") +
      scale_color_brewer(palette = "Set2", name = "Feature\nCategory") +
      theme_minimal() +
      labs(
        title = "B. Feature Importance vs Tissue Variability",
        x = "Variance Across Tissue Types",
        y = "Importance Score (Selection Frequency)",
        subtitle = "Relationship between tissue discrimination and feature importance"
      ) +
      theme(
        plot.title = element_text(size = 16, face = "bold"),
        plot.subtitle = element_text(size = 12),
        legend.position = "right",
        axis.title = element_text(size = 12, face = "bold")
      )
    
    return(variability_plot)
  }
  
  create_feature_correlation_matrix <- function() {
    extract_consensus_features <- function(target_results, n = 20) {
      feature_counts <- list()
      for(tissue in names(target_results)) {
        if(!is.null(target_results[[tissue]]$feature_counts)) {
          counts <- target_results[[tissue]]$feature_counts
          for(feat in names(counts)) {
            if(is.null(feature_counts[[feat]])) feature_counts[[feat]] <- 0
            feature_counts[[feat]] <- feature_counts[[feat]] + counts[[feat]]
          }
        }
      }
      
      sorted_features <- sort(unlist(feature_counts), decreasing = TRUE)
      return(names(sorted_features)[1:min(n, length(sorted_features))])
    }
    
    consensus_features <- extract_consensus_features(all_results[["SMRIN"]], 15)
    
    if(length(consensus_features) > 1) {
      feature_matrix <- as.matrix(merged_gtex[, ..consensus_features])
      feature_matrix <- feature_matrix[complete.cases(feature_matrix), ]
      
      if(nrow(feature_matrix) > 10) {
        correlation_matrix <- cor(feature_matrix, use = "complete.obs")
        
        cor_long <- reshape2::melt(correlation_matrix)
        cor_long$Var1 <- sub("feature_", "", cor_long$Var1)
        cor_long$Var2 <- sub("feature_", "", cor_long$Var2)
        
        correlation_plot <- ggplot(cor_long, aes(x = Var1, y = Var2, fill = value)) +
          geom_tile(color = "white", size = 0.5) +
          scale_fill_gradient2(low = "#2166AC", mid = "white", high = "#D73027", 
                              midpoint = 0, name = "Correlation\nCoefficient",
                              limits = c(-1, 1)) +
          theme_minimal() +
          theme(
            axis.text.x = element_text(angle = 45, hjust = 1, size = 10),
            axis.text.y = element_text(size = 10),
            plot.title = element_text(size = 16, face = "bold"),
            plot.subtitle = element_text(size = 12),
            legend.position = "right"
          ) +
          labs(
            title = "C. Feature Correlation Matrix",
            x = "Feature ID",
            y = "Feature ID",
            subtitle = "Correlation structure among top predictive features"
          ) +
          coord_fixed()
        
        return(correlation_plot)
      }
    }
    
    empty_plot <- ggplot() + 
      annotate("text", x = 0.5, y = 0.5, 
               label = "Insufficient data\nfor correlation analysis", 
               size = 6, hjust = 0.5) +
      theme_void() +
      labs(title = "C. Feature Correlation Matrix") +
      theme(plot.title = element_text(size = 16, face = "bold", hjust = 0.5))
    
    return(empty_plot)
  }
  
  panel_a <- analyze_feature_categories()
  panel_b <- create_tissue_variability_analysis()
  panel_c <- create_feature_correlation_matrix()
  
  top_panels <- grid.arrange(panel_a, panel_b, ncol = 2)
  figure8_combined <- grid.arrange(top_panels, panel_c, nrow = 2, heights = c(1, 1))
  
  return(figure8_combined)
}

analyze_biological_significance <- function(all_results) {
  for(target in c("SMRIN", "SMATSSCR")) {
    all_features <- unlist(lapply(all_results[[target]], function(x) {
      if(!is.null(x$feature_counts)) names(x$feature_counts) else NULL
    }))
    
    feature_nums <- as.numeric(sub("feature_", "", all_features))
    mean_features <- sum(feature_nums <= 1024)
    std_features <- sum(feature_nums > 1024 & feature_nums <= 2048)
    min_features <- sum(feature_nums > 2048 & feature_nums <= 3072)
    max_features <- sum(feature_nums > 3072)
  }
}

figure8_plot <- create_biological_interpretation_analysis(all_results, merged_gtex)

analyze_biological_significance(all_results)

ggsave(file.path(output_path, "Figure8_Biological_Interpretation.pdf"), 
       figure8_plot, width = 16, height = 14, dpi = 600, bg = "white")

#—————————————————————————— # 12. Supplementary Figure S1: Quality Score Distributions by Tissue #——————————————————————————

create_quality_score_distributions <- function(merged_gtex) {
  
  prepare_quality_data <- function() {
    rin_data <- merged_gtex[!is.na(SMRIN), .(tissue_main, SMRIN)]
    setnames(rin_data, "SMRIN", "Score")
    rin_data$Score_Type <- "RIN Score"
    
    auto_data <- merged_gtex[!is.na(SMATSSCR), .(tissue_main, SMATSSCR)]
    setnames(auto_data, "SMATSSCR", "Score")
    auto_data$Score_Type <- "Autolysis Score"
    
    return(list(rin = rin_data, autolysis = auto_data))
  }
  
  get_top_tissues <- function(n_tissues = 20) {
    tissue_counts <- merged_gtex[, .N, by = tissue_main][order(-N)]
    return(head(tissue_counts$tissue_main, n_tissues))
  }
  
  quality_data <- prepare_quality_data()
  top_tissues <- get_top_tissues()
  
  rin_filtered <- quality_data$rin[tissue_main %in% top_tissues]
  auto_filtered <- quality_data$autolysis[tissue_main %in% top_tissues]
  
  rin_filtered$tissue_main <- factor(rin_filtered$tissue_main, levels = top_tissues)
  auto_filtered$tissue_main <- factor(auto_filtered$tissue_main, levels = top_tissues)
  
  create_rin_distribution_panel <- function() {
    rin_violin <- ggplot(rin_filtered, aes(x = tissue_main, y = Score)) +
      geom_violin(fill = "#66CDAA", alpha = 0.8, trim = FALSE, scale = "width") +
      geom_boxplot(width = 0.15, alpha = 0.9, outlier.size = 1, outlier.alpha = 0.6) +
      stat_summary(fun = median, geom = "point", color = "darkblue", size = 1.5) +
      theme_minimal() +
      theme(
        axis.text.x = element_text(angle = 45, hjust = 1, size = 11, face = "bold"),
        plot.title = element_text(size = 18, face = "bold"),
        plot.subtitle = element_text(size = 12),
        axis.title = element_text(size = 12, face = "bold")
      ) +
      labs(
        title = "A. RIN Score Distributions by Tissue Type",
        subtitle = "RNA Integrity Number across major tissue types",
        x = NULL,
        y = "RIN Score"
      ) +
      scale_y_continuous(limits = c(1, 10), breaks = seq(2, 10, 2))
    
    return(rin_violin)
  }
  
  create_autolysis_distribution_panel <- function() {
    auto_violin <- ggplot(auto_filtered, aes(x = tissue_main, y = Score)) +
      geom_violin(fill = "#FC8D62", alpha = 0.8, trim = FALSE, scale = "width") +
      geom_boxplot(width = 0.15, alpha = 0.9, outlier.size = 1, outlier.alpha = 0.6) +
      stat_summary(fun = median, geom = "point", color = "darkred", size = 1.5) +
      theme_minimal() +
      theme(
        axis.text.x = element_text(angle = 45, hjust = 1, size = 11, face = "bold"),
        plot.title = element_text(size = 18, face = "bold"),
        plot.subtitle = element_text(size = 12),
        axis.title = element_text(size = 12, face = "bold")
      ) +
      labs(
        title = "B. Autolysis Score Distributions by Tissue Type",
        subtitle = "Tissue degradation levels across major tissue types",
        x = "Tissue Type",
        y = "Autolysis Score"
      ) +
      scale_y_continuous(limits = c(0, 3), breaks = 0:3,
                        labels = c("0 (None)", "1 (Mild)", "2 (Moderate)", "3 (Severe)"))
    
    return(auto_violin)
  }
  
  calculate_distribution_stats <- function() {
    rin_stats <- rin_filtered[, .(
      mean_score = round(mean(Score, na.rm = TRUE), 2),
      median_score = round(median(Score, na.rm = TRUE), 2),
      sd_score = round(sd(Score, na.rm = TRUE), 2),
      sample_size = .N
    ), by = tissue_main][order(-mean_score)]
    
    auto_stats <- auto_filtered[, .(
      mean_score = round(mean(Score, na.rm = TRUE), 2),
      median_score = round(median(Score, na.rm = TRUE), 2),
      sd_score = round(sd(Score, na.rm = TRUE), 2),
      sample_size = .N
    ), by = tissue_main][order(mean_score)]
    
    return(list(rin_stats = rin_stats, auto_stats = auto_stats))
  }
  
  panel_a <- create_rin_distribution_panel()
  panel_b <- create_autolysis_distribution_panel()
  distribution_stats <- calculate_distribution_stats()
  
  supp_fig_s1 <- grid.arrange(panel_a, panel_b, nrow = 2)
  
  return(supp_fig_s1)
}

analyze_quality_correlations <- function(merged_gtex) {
  quality_subset <- merged_gtex[!is.na(SMRIN) & !is.na(SMATSSCR)]
  
  if(nrow(quality_subset) > 10) {
    correlation_test <- cor.test(quality_subset$SMRIN, quality_subset$SMATSSCR)
    return(correlation_test)
  } else {
    return(NULL)
  }
}

supp_figure_s1 <- create_quality_score_distributions(merged_gtex)

quality_correlations <- analyze_quality_correlations(merged_gtex)

ggsave(file.path(output_path, "SuppFig_S1_Quality_Distributions.pdf"), 
       supp_figure_s1, width = 18, height = 12, dpi = 600, bg = "white")

#—————————————————————————— # 13. Supplementary Figure S2: Cross-validation Stability Analysis #——————————————————————————

create_cv_stability_analysis <- function(all_results) {
  
  extract_fold_performance <- function(target_results) {
    fold_performance <- data.frame()
    
    for(tissue in names(target_results)) {
      result <- target_results[[tissue]]
      if(!is.null(result) && !is.null(result$fold_results)) {
        
        for(fold_result in result$fold_results) {
          fold_data <- data.frame(
            tissue = tissue,
            fold = fold_result$fold,
            r_value = abs(fold_result$r),
            rmse = fold_result$rmse,
            mae = fold_result$mae
          )
          fold_performance <- rbind(fold_performance, fold_data)
        }
      }
    }
    
    return(fold_performance)
  }
  
  rin_fold_data <- extract_fold_performance(all_results[["SMRIN"]])
  auto_fold_data <- extract_fold_performance(all_results[["SMATSSCR"]])
  
  rin_fold_data$target <- "RIN"
  auto_fold_data$target <- "Autolysis"
  combined_fold_data <- rbind(rin_fold_data, auto_fold_data)
  
  create_stability_scatter <- function() {
    stability_metrics <- combined_fold_data %>%
      group_by(tissue, target) %>%
      summarise(
        mean_r = mean(r_value, na.rm = TRUE),
        sd_r = sd(r_value, na.rm = TRUE),
        cv_r = sd(r_value, na.rm = TRUE) / mean(r_value, na.rm = TRUE),
        .groups = 'drop'
      ) %>%
      filter(is.finite(cv_r) & cv_r > 0)
    
    stability_plot <- ggplot(stability_metrics, aes(x = mean_r, y = cv_r, color = target)) +
      geom_point(size = 3.5, alpha = 0.8) +
      geom_smooth(method = "lm", se = TRUE, alpha = 0.3) +
      scale_color_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
      theme_minimal() +
      labs(
        title = "A. Model Stability vs Performance",
        x = "Mean |R| Across Folds",
        y = "Coefficient of Variation (|R|)",
        color = "Model Type",
        subtitle = "Lower CV indicates more stable cross-validation performance"
      ) +
      theme(
        plot.title = element_text(size = 16, face = "bold"),
        plot.subtitle = element_text(size = 12),
        legend.position = "top",
        axis.title = element_text(size = 12, face = "bold")
      )
    
    return(stability_plot)
  }
  
  create_fold_variability_boxplots <- function() {
    top_tissues_rin <- combined_fold_data %>% 
      filter(target == "RIN") %>%
      group_by(tissue) %>%
      summarise(mean_r = mean(r_value, na.rm = TRUE), .groups = 'drop') %>%
      arrange(desc(mean_r)) %>% 
      slice(1:8) %>% 
      pull(tissue)
    
    top_tissues_auto <- combined_fold_data %>% 
      filter(target == "Autolysis") %>%
      group_by(tissue) %>%
      summarise(mean_r = mean(r_value, na.rm = TRUE), .groups = 'drop') %>%
      arrange(desc(mean_r)) %>% 
      slice(1:8) %>% 
      pull(tissue)
    
    fold_subset <- combined_fold_data %>%
      filter((target == "RIN" & tissue %in% top_tissues_rin) |
             (target == "Autolysis" & tissue %in% top_tissues_auto))
    
    variability_plot <- ggplot(fold_subset, aes(x = tissue, y = r_value, fill = target)) +
      geom_boxplot(alpha = 0.8, outlier.alpha = 0.6) +
      facet_wrap(~target, scales = "free_x", ncol = 1) +
      scale_fill_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
      theme_minimal() +
      theme(
        axis.text.x = element_text(angle = 45, hjust = 1, size = 10),
        legend.position = "none",
        strip.text = element_text(size = 14, face = "bold"),
        plot.title = element_text(size = 16, face = "bold"),
        axis.title = element_text(size = 12, face = "bold")
      ) +
      labs(
        title = "B. Cross-Fold Performance Variability",
        x = "Tissue Type",
        y = "|R| Value Across Folds",
        subtitle = "Box plots show distribution of performance across CV folds"
      )
    
    return(variability_plot)
  }
  
  create_stability_ranking <- function() {
    stability_ranking <- combined_fold_data %>%
      group_by(tissue, target) %>%
      summarise(
        mean_r = mean(r_value, na.rm = TRUE),
        cv_r = sd(r_value, na.rm = TRUE) / mean(r_value, na.rm = TRUE),
        .groups = 'drop'
      ) %>%
      filter(is.finite(cv_r) & cv_r > 0) %>%
      arrange(cv_r) %>%
      mutate(
        stability_rank = row_number(),
        stability_category = cut(cv_r, 
                               breaks = c(0, 0.1, 0.2, 0.5, Inf),
                               labels = c("Very Stable", "Stable", "Moderate", "Unstable"))
      )
    
    ranking_plot <- ggplot(stability_ranking, aes(x = stability_rank, y = cv_r, color = target)) +
      geom_point(size = 3, alpha = 0.8) +
      geom_hline(yintercept = c(0.1, 0.2, 0.5), linetype = "dashed", alpha = 0.6) +
      scale_color_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
      theme_minimal() +
      labs(
        title = "C. Model Stability Ranking",
        x = "Stability Rank (1 = Most Stable)",
        y = "Coefficient of Variation (|R|)",
        color = "Model Type",
        subtitle = "Horizontal lines indicate stability thresholds"
      ) +
      theme(
        plot.title = element_text(size = 16, face = "bold"),
        plot.subtitle = element_text(size = 12),
        legend.position = "none",
        axis.title = element_text(size = 12, face = "bold")
      )
    
    return(ranking_plot)
  }
  
  panel_a <- create_stability_scatter()
  panel_b <- create_fold_variability_boxplots()
  panel_c <- create_stability_ranking()
  
  top_panels <- grid.arrange(panel_a, panel_c, ncol = 2)
  supp_fig_s2 <- grid.arrange(top_panels, panel_b, nrow = 2, heights = c(1, 1.5))
  
  return(supp_fig_s2)
}

calculate_stability_stats <- function(all_results) {
  stability_summary <- data.frame()
  
  for(target in c("SMRIN", "SMATSSCR")) {
    for(tissue in names(all_results[[target]])) {
      result <- all_results[[target]][[tissue]]
      if(!is.null(result) && !is.null(result$fold_results)) {
        
        fold_r_values <- sapply(result$fold_results, function(x) abs(x$r))
        
        stability_row <- data.frame(
          tissue = tissue,
          target = target,
          mean_r = mean(fold_r_values),
          cv_r = sd(fold_r_values) / mean(fold_r_values),
          min_r = min(fold_r_values),
          max_r = max(fold_r_values)
        )
        stability_summary <- rbind(stability_summary, stability_row)
      }
    }
  }
  
  by_target <- stability_summary %>%
    group_by(target) %>%
    summarise(
      mean_cv = round(mean(cv_r, na.rm = TRUE), 3),
      median_cv = round(median(cv_r, na.rm = TRUE), 3),
      stable_models = sum(cv_r < 0.2, na.rm = TRUE),
      total_models = n(),
      .groups = 'drop'
    )
  
  return(stability_summary)
}

supp_figure_s2 <- create_cv_stability_analysis(all_results)

stability_stats <- calculate_stability_stats(all_results)

ggsave(file.path(output_path, "SuppFig_S2_CV_Stability.pdf"), 
       supp_figure_s2, width = 14, height = 12, dpi = 600, bg = "white")

#—————————————————————————— # 14. Supplementary Figure S3: Extended Comparative Analysis #——————————————————————————

create_extended_comparative_analysis <- function(all_results, rin_summary, autolysis_summary) {
  
  create_cross_model_correlation <- function() {
    common_tissues <- intersect(rin_summary$Tissue, autolysis_summary$Tissue)
    
    comparison_data <- data.frame(
      Tissue = common_tissues,
      RIN_R = rin_summary$R[match(common_tissues, rin_summary$Tissue)],
      Auto_R = autolysis_summary$R[match(common_tissues, autolysis_summary$Tissue)],
      RIN_RMSE = rin_summary$RMSE[match(common_tissues, rin_summary$Tissue)],
      Auto_RMSE = autolysis_summary$RMSE[match(common_tissues, autolysis_summary$Tissue)]
    )
    
    r_correlation <- cor(abs(comparison_data$RIN_R), abs(comparison_data$Auto_R), use = "complete.obs")
    
    correlation_plot <- ggplot(comparison_data, aes(x = abs(RIN_R), y = abs(Auto_R))) +
      geom_point(size = 3.5, alpha = 0.8, color = "darkblue") +
      geom_smooth(method = "lm", se = TRUE, color = "red", alpha = 0.3) +
      geom_abline(intercept = 0, slope = 1, linetype = "dashed", color = "gray", size = 1) +
      theme_minimal() +
      labs(
        title = "A. Cross-Model Performance Correlation",
        x = "RIN Model |R|",
        y = "Autolysis Model |R|",
        subtitle = paste("Correlation =", round(r_correlation, 3))
      ) +
      theme(
        plot.title = element_text(size = 16, face = "bold"),
        plot.subtitle = element_text(size = 12),
        axis.title = element_text(size = 12, face = "bold")
      ) +
      coord_fixed()
    
    return(correlation_plot)
  }
  
  create_sample_size_performance <- function() {
    combined_data <- rbind(
      data.frame(rin_summary[, c("Tissue", "Samples", "R")], Model = "RIN"),
      data.frame(autolysis_summary[, c("Tissue", "Samples", "R")], Model = "Autolysis")
    )
    
    combined_data$R_abs <- abs(combined_data$R)
    combined_data$Size_Bin <- cut(combined_data$Samples, 
                                 breaks = c(0, 50, 100, 200, 500, Inf),
                                 labels = c("≤50", "51-100", "101-200", "201-500", ">500"))
    
    size_performance_plot <- ggplot(combined_data, aes(x = Size_Bin, y = R_abs, fill = Model)) +
      geom_boxplot(alpha = 0.8, outlier.alpha = 0.6) +
      scale_fill_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
      theme_minimal() +
      labs(
        title = "B. Model Performance by Sample Size Category",
        x = "Sample Size Category",
        y = "Absolute Correlation Coefficient |R|",
        subtitle = "Performance distribution across sample size bins"
      ) +
      theme(
        plot.title = element_text(size = 16, face = "bold"),
        plot.subtitle = element_text(size = 12),
        legend.position = "top",
        axis.title = element_text(size = 12, face = "bold")
      )
    
    return(size_performance_plot)
  }
  
  create_tissue_preference_analysis <- function() {
    common_tissues <- intersect(rin_summary$Tissue, autolysis_summary$Tissue)
    
    preference_data <- data.frame(
      Tissue = common_tissues,
      RIN_R = abs(rin_summary$R[match(common_tissues, rin_summary$Tissue)]),
      Auto_R = abs(autolysis_summary$R[match(common_tissues, autolysis_summary$Tissue)])
    )
    
    preference_data$Better_Model <- ifelse(
      preference_data$RIN_R > preference_data$Auto_R, "RIN", "Autolysis"
    )
    preference_data$Performance_Difference <- abs(preference_data$RIN_R - preference_data$Auto_R)
    preference_data$Max_R <- pmax(preference_data$RIN_R, preference_data$Auto_R)
    
    preference_data$Tissue_System <- case_when(
      grepl("Brain|Nerve|Spinal", preference_data$Tissue) ~ "Nervous",
      grepl("Heart|Artery|Aorta", preference_data$Tissue) ~ "Cardiovascular", 
      grepl("Lung|Trachea", preference_data$Tissue) ~ "Respiratory",
      grepl("Liver|Pancreas|Stomach|Colon|Small|Esophagus", preference_data$Tissue) ~ "Digestive",
      grepl("Kidney|Bladder", preference_data$Tissue) ~ "Urinary",
      grepl("Muscle|Skin", preference_data$Tissue) ~ "Musculoskeletal",
      grepl("Thyroid|Adrenal|Pituitary", preference_data$Tissue) ~ "Endocrine",
      TRUE ~ "Other"
    )
    
    preference_plot <- ggplot(preference_data, aes(x = Max_R, y = Performance_Difference, 
                                                  color = Better_Model, shape = Tissue_System)) +
      geom_point(size = 4, alpha = 0.8) +
      scale_color_manual(values = c("RIN" = "#66CDAA", "Autolysis" = "#FC8D62")) +
      scale_shape_manual(values = c(16, 17, 18, 15, 3, 4, 8, 1)[1:length(unique(preference_data$Tissue_System))]) +
      theme_minimal() +
      labs(
        title = "C. Tissue Categories and Model Preference",
        x = "Maximum |R| Value",
        y = "Absolute Difference in |R|",
        color = "Better Model",
        shape = "Tissue System",
        subtitle = "Model preference patterns across biological systems"
      ) +
      theme(
        plot.title = element_text(size = 16, face = "bold"),
        plot.subtitle = element_text(size = 12),
        legend.position = "right",
        axis.title = element_text(size = 12, face = "bold")
      )
    
    return(preference_plot)
  }
  
  create_feature_overlap_analysis <- function() {
    extract_features <- function(target_results, threshold = 5) {
      feature_counts <- list()
      for(tissue in names(target_results)) {
        if(!is.null(target_results[[tissue]]$feature_counts)) {
          counts <- target_results[[tissue]]$feature_counts
          for(feat in names(counts)) {
            if(is.null(feature_counts[[feat]])) feature_counts[[feat]] <- 0
            feature_counts[[feat]] <- feature_counts[[feat]] + counts[[feat]]
          }
        }
      }
      
      important_features <- names(feature_counts)[feature_counts >= threshold]
      return(important_features)
    }
    
    thresholds <- c(3, 5, 10, 15, 20)
    overlap_data <- data.frame()
    
    for(thresh in thresholds) {
      rin_thresh <- extract_features(all_results[["SMRIN"]], thresh)
      auto_thresh <- extract_features(all_results[["SMATSSCR"]], thresh)
      
      overlap_count <- length(intersect(rin_thresh, auto_thresh))
      rin_only <- length(setdiff(rin_thresh, auto_thresh))
      auto_only <- length(setdiff(auto_thresh, rin_thresh))
      
      overlap_data <- rbind(overlap_data, data.frame(
        Threshold = thresh,
        Overlap = overlap_count,
        RIN_Only = rin_only,
        Auto_Only = auto_only
      ))
    }
    
    overlap_long <- reshape2::melt(overlap_data, id.vars = "Threshold", 
                                  variable.name = "Category", value.name = "Count")
    
    overlap_plot <- ggplot(overlap_long, aes(x = factor(Threshold), y = Count, fill = Category)) +
      geom_bar(stat = "identity", position = "stack") +
      scale_fill_manual(values = c("Overlap" = "#8E44AD", "RIN_Only" = "#66CDAA", "Auto_Only" = "#FC8D62"),
                       labels = c("Common Features", "RIN Only", "Autolysis Only")) +
      theme_minimal() +
      labs(
        title = "D. Feature Overlap at Different Selection Thresholds",
        x = "Minimum Selection Frequency",
        y = "Number of Features",
        fill = "Feature Type",
        subtitle = "Overlap analysis across selection frequency thresholds"
      ) +
      theme(
        plot.title = element_text(size = 16, face = "bold"),
        plot.subtitle = element_text(size = 12),
        legend.position = "top",
        axis.title = element_text(size = 12, face = "bold")
      )
    
    return(overlap_plot)
  }
  
  panel_a <- create_cross_model_correlation()
  panel_b <- create_sample_size_performance()
  panel_c <- create_tissue_preference_analysis()
  panel_d <- create_feature_overlap_analysis()
  
  combined_plot <- grid.arrange(panel_a, panel_b, panel_c, panel_d, nrow = 2, ncol = 2)
  
  return(combined_plot)
}

create_model_comparison_summary <- function(rin_summary, autolysis_summary) {
  common_tissues <- intersect(rin_summary$Tissue, autolysis_summary$Tissue)
  
  comparison_summary <- data.frame(
    Tissue = common_tissues,
    RIN_R = abs(rin_summary$R[match(common_tissues, rin_summary$Tissue)]),
    Auto_R = abs(autolysis_summary$R[match(common_tissues, autolysis_summary$Tissue)])
  )
  
  comparison_summary$Better_Model <- ifelse(
    comparison_summary$RIN_R > comparison_summary$Auto_R, "RIN", "Autolysis"
  )
  
  model_preference <- table(comparison_summary$Better_Model)
  
  return(list(
    summary = comparison_summary,
    preference_counts = model_preference,
    mean_performance = c(
      RIN = mean(comparison_summary$RIN_R, na.rm = TRUE),
      Autolysis = mean(comparison_summary$Auto_R, na.rm = TRUE)
    )
  ))
}

supp_figure_s3 <- create_extended_comparative_analysis(all_results, rin_summary, autolysis_summary)

comparison_summary <- create_model_comparison_summary(rin_summary, autolysis_summary)

ggsave(file.path(output_path, "SuppFig_S3_Extended_Comparison.pdf"), 
       supp_figure_s3, width = 16, height = 14, dpi = 600, bg = "white")
G2;H2;Warningh in grid.Call.graphics(C_text, as.graphicsAnnot(x$label), x$x, x$y,  :
  for '≤50' in 'mbcsToSbcs': <= substituted for ≤ (U+2264)g

#—————————————————————————— # 15. Comprehensive Final Report Generation #——————————————————————————

generate_comprehensive_report <- function() {
  report_path <- file.path(data_path, "processed/comprehensive_gtex_report.txt")
  
  sink(report_path)
  
  cat("================================================================================\n")
  cat("                    COMPREHENSIVE GTEx ANALYSIS REPORT                          \n")
  cat("================================================================================\n\n")
  
  # Header Information
  cat("ANALYSIS OVERVIEW\n")
  cat("-----------------\n")
  cat(paste("Report Generated:", Sys.time(), "\n"))
  cat(paste("Pipeline Version: GTEx Analysis Pipeline v1.0\n"))
  cat(paste("Analysis Duration:", format(Sys.time() - start_time, digits = 3), "\n\n"))
  
  # Dataset Summary
  cat("DATASET SUMMARY\n")
  cat("===============\n")
  cat(paste("Total Samples Analyzed:", nrow(merged_gtex), "\n"))
  cat(paste("Total Tissue Types:", length(unique(merged_gtex$tissue_main)), "\n"))
  cat(paste("Total Features Extracted:", length(grep("^feature_", names(merged_gtex))), "\n"))
  cat(paste("Male Subjects:", sum(merged_gtex$sex == "male"), "\n"))
  cat(paste("Female Subjects:", sum(merged_gtex$sex == "female"), "\n\n"))
  
  # Top tissue types by sample count
  tissue_counts <- merged_gtex[, .N, by = tissue_main][order(-N)]
  cat("Top 10 Tissue Types by Sample Count:\n")
  for(i in 1:min(10, nrow(tissue_counts))) {
    cat(sprintf("  %2d. %-25s: %4d samples\n", i, tissue_counts$tissue_main[i], tissue_counts$N[i]))
  }
  cat("\n")
  
  # Quality Metrics Analysis
  cat("QUALITY METRICS ANALYSIS\n")
  cat("========================\n")
  
  # RIN Analysis
  rin_data <- merged_gtex[!is.na(SMRIN)]
  if(nrow(rin_data) > 0) {
    cat("RIN Score Distribution:\n")
    cat(paste("  Samples with RIN data:", nrow(rin_data), "of", nrow(merged_gtex), 
              sprintf("(%.1f%%)\n", 100 * nrow(rin_data) / nrow(merged_gtex))))
    cat(paste("  Mean RIN Score:", round(mean(rin_data$SMRIN), 2), "\n"))
    cat(paste("  Median RIN Score:", round(median(rin_data$SMRIN), 2), "\n"))
    cat(paste("  RIN Range:", round(min(rin_data$SMRIN), 2), "-", round(max(rin_data$SMRIN), 2), "\n"))
    cat(paste("  High Quality (RIN ≥ 7.0):", sum(rin_data$SMRIN >= 7.0), 
              sprintf("(%.1f%%)\n", 100 * sum(rin_data$SMRIN >= 7.0) / nrow(rin_data))))
  }
  
  # Autolysis Analysis
  autolysis_data <- merged_gtex[!is.na(SMATSSCR)]
  if(nrow(autolysis_data) > 0) {
    cat("\nAutolysis Score Distribution:\n")
    cat(paste("  Samples with Autolysis data:", nrow(autolysis_data), "of", nrow(merged_gtex),
              sprintf("(%.1f%%)\n", 100 * nrow(autolysis_data) / nrow(merged_gtex))))
    cat(paste("  Mean Autolysis Score:", round(mean(autolysis_data$SMATSSCR), 2), "\n"))
    cat(paste("  Median Autolysis Score:", round(median(autolysis_data$SMATSSCR), 2), "\n"))
    autolysis_counts <- table(autolysis_data$SMATSSCR)
    for(score in names(autolysis_counts)) {
      cat(sprintf("  Score %s: %d samples (%.1f%%)\n", score, autolysis_counts[score],
                  100 * autolysis_counts[score] / nrow(autolysis_data)))
    }
    cat(paste("  High Quality (Score ≤ 2):", sum(autolysis_data$SMATSSCR <= 2),
              sprintf("(%.1f%%)\n", 100 * sum(autolysis_data$SMATSSCR <= 2) / nrow(autolysis_data))))
  }
  
  # Dimensionality Reduction Results
  cat("\nDIMENSIONALITY REDUCTION ANALYSIS\n")
  cat("=================================\n")
  
  if(exists("pca_results")) {
    cat("Principal Component Analysis:\n")
    cat(paste("  PC1 Variance Explained:", sprintf("%.1f%%", pca_results$variance_explained[1] * 100), "\n"))
    cat(paste("  PC2 Variance Explained:", sprintf("%.1f%%", pca_results$variance_explained[2] * 100), "\n"))
    cat(paste("  First 10 PCs Cumulative Variance:", sprintf("%.1f%%", pca_results$cumulative_variance[10] * 100), "\n"))
    cat(paste("  First 50 PCs Cumulative Variance:", sprintf("%.1f%%", pca_results$cumulative_variance[50] * 100), "\n"))
  }
  
  if(exists("tissue_sil")) {
    cat("\nUMAP Clustering Analysis:\n")
    cat(paste("  Overall Silhouette Score:", sprintf("%.3f", mean(sil[,3], na.rm = TRUE)), "\n"))
    cat("  Top 5 Best-Separated Tissues (by Silhouette Score):\n")
    top_silhouette <- head(tissue_sil[order(-avg_silhouette)], 5)
    for(i in 1:nrow(top_silhouette)) {
      cat(sprintf("    %d. %-25s: %.3f\n", i, top_silhouette$tissue[i], top_silhouette$avg_silhouette[i]))
    }
  }
  
  # Variance Analysis Results
  cat("\nVARIANCE ANALYSIS (ANOVA)\n")
  cat("=========================\n")
  
  if(exists("rin_variance")) {
    cat("RIN Score Variance Explained by Demographics:\n")
    rin_variance_sorted <- rin_variance[order(-rin_variance$var_explained),]
    for(i in 1:nrow(rin_variance_sorted)) {
      cat(sprintf("  %-20s: %5.1f%% (p %s)\n", 
                  rin_variance_sorted$label[i], 
                  rin_variance_sorted$var_explained[i],
                  rin_variance_sorted$significance[i]))
    }
  }
  
  if(exists("autolysis_variance")) {
    cat("\nAutolysis Score Variance Explained by Demographics:\n")
    autolysis_variance_sorted <- autolysis_variance[order(-autolysis_variance$var_explained),]
    for(i in 1:nrow(autolysis_variance_sorted)) {
      cat(sprintf("  %-20s: %5.1f%% (p %s)\n", 
                  autolysis_variance_sorted$label[i], 
                  autolysis_variance_sorted$var_explained[i],
                  autolysis_variance_sorted$significance[i]))
    }
  }
  
  # Correlation Analysis
  cat("\nCORRELATION ANALYSIS\n")
  cat("====================\n")
  
  if(exists("tissue_corr")) {
    cat("RIN vs Autolysis Correlations by Tissue:\n")
    tissue_corr_sorted <- tissue_corr[order(-abs(tissue_corr$correlation)),]
    cat("  Strongest Correlations (Top 10):\n")
    top_corr <- head(tissue_corr_sorted, 10)
    for(i in 1:nrow(top_corr)) {
      cat(sprintf("    %-25s: r = %6.3f (p %s, n = %d)\n", 
                  top_corr$tissue_main[i], 
                  top_corr$correlation[i],
                  top_corr$significance[i],
                  top_corr$n_samples[i]))
    }
  }
  
  # Machine Learning Model Results
  cat("\nMACHINE LEARNING MODEL PERFORMANCE\n")
  cat("==================================\n")
  
  if(exists("all_results")) {
    # RIN Models
    if("SMRIN" %in% names(all_results)) {
      rin_models <- all_results[["SMRIN"]]
      rin_r_values <- sapply(rin_models, function(x) {
        if(is.null(x)) return(NA)
        return(abs(x$performance$pooled_r))  # Use absolute R
        # return(sqrt(x$performance$pooled_r2)) # use when required for R²
      })
      rin_r_values <- rin_r_values[!is.na(rin_r_values)]
      
      cat("RIN Score Prediction Models:\n")
      cat(paste("  Successful models:", length(rin_r_values), "tissues\n"))
      cat(paste("  Mean |R|:", sprintf("%.3f", mean(rin_r_values)), "\n"))
      cat(paste("  Median |R|:", sprintf("%.3f", median(rin_r_values)), "\n"))
      cat(paste("  Strong models (|R| > 0.5):", sum(rin_r_values > 0.5), "\n"))
      cat(paste("  Excellent models (|R| > 0.7):", sum(rin_r_values > 0.7), "\n"))
      
      # Top performing tissues
      cat("  Top 5 RIN Prediction Models:\n")
      top_rin <- names(sort(rin_r_values, decreasing = TRUE))[1:5]
      for(i in 1:length(top_rin)) {
        tissue_name <- top_rin[i]
        r_value <- rin_r_values[tissue_name]
        cat(sprintf("    %d. %-25s: |R| = %.3f\n", i, tissue_name, r_value))
      }
    }
    
    # Autolysis Models
    if("SMATSSCR" %in% names(all_results)) {
      auto_models <- all_results[["SMATSSCR"]]
      auto_r_values <- sapply(auto_models, function(x) {
        if(is.null(x)) return(NA)
        return(sqrt(x$performance$pooled_r2))
      })
      auto_r_values <- auto_r_values[!is.na(auto_r_values)]
      
      cat("\nAutolysis Score Prediction Models:\n")
      cat(paste("  Successful models:", length(auto_r_values), "tissues\n"))
      cat(paste("  Mean R:", sprintf("%.3f", mean(auto_r_values)), "\n"))
      cat(paste("  Median R:", sprintf("%.3f", median(auto_r_values)), "\n"))
      cat(paste("  Strong models (R > 0.5):", sum(auto_r_values > 0.5), "\n"))
      cat(paste("  Excellent models (R > 0.7):", sum(auto_r_values > 0.7), "\n"))
      
      # Top performing tissues
      cat("  Top 5 Autolysis Prediction Models:\n")
      top_auto <- names(sort(auto_r_values, decreasing = TRUE))[1:5]
      for(i in 1:length(top_auto)) {
        tissue_name <- top_auto[i]
        r_value <- auto_r_values[tissue_name]
        cat(sprintf("    %d. %-25s: R = %.3f\n", i, tissue_name, r_value))
      }
    }
  }
  
  # Feature Analysis
  cat("\nFEATURE IMPORTANCE ANALYSIS\n")
  cat("===========================\n")
  
  if(exists("common_features") && exists("rin_only_features") && exists("auto_only_features")) {
    cat("Feature Overlap Analysis:\n")
    cat(paste("  Common features (both models):", length(common_features), "\n"))
    cat(paste("  RIN-specific features:", length(rin_only_features), "\n"))
    cat(paste("  Autolysis-specific features:", length(auto_only_features), "\n"))
    
    total_unique <- length(common_features) + length(rin_only_features) + length(auto_only_features)
    cat(paste("  Feature overlap percentage:", sprintf("%.1f%%", 100 * length(common_features) / total_unique), "\n"))
  }
  
  # Esophagus Analysis
  if(exists("esophagus_samples")) {
    cat("\nESOPHAGUS SUBTYPE ANALYSIS\n")
    cat("==========================\n")
    esophagus_counts <- table(esophagus_samples$tissue_sub)
    cat("Esophagus Subtype Distribution:\n")
    for(subtype in names(esophagus_counts)) {
      cat(sprintf("  %-30s: %3d samples\n", subtype, esophagus_counts[subtype]))
    }
  }
  
  # Cross-Validation Stability
  if(exists("stability_stats")) {
    cat("\nMODEL STABILITY ANALYSIS\n")
    cat("========================\n")
    
    stable_rin <- sum(stability_stats$cv_r[stability_stats$target == "SMRIN"] < 0.2, na.rm = TRUE)
    total_rin <- sum(stability_stats$target == "SMRIN")
    stable_auto <- sum(stability_stats$cv_r[stability_stats$target == "SMATSSCR"] < 0.2, na.rm = TRUE)
    total_auto <- sum(stability_stats$target == "SMATSSCR")
    
    cat("Cross-Validation Stability (CV < 0.2 = Stable):\n")
    cat(sprintf("  RIN Models: %d/%d stable (%.1f%%)\n", stable_rin, total_rin, 100 * stable_rin / total_rin))
    cat(sprintf("  Autolysis Models: %d/%d stable (%.1f%%)\n", stable_auto, total_auto, 100 * stable_auto / total_auto))
  }
  
  # Summary Statistics
  cat("\nSUMMARY STATISTICS\n")
  cat("==================\n")
  
  # Files generated
  output_files <- list.files(output_path, pattern = "*.pdf", full.names = FALSE)
  cat(paste("Generated", length(output_files), "visualization files:\n"))
  for(file in output_files) {
    cat(paste("  -", file, "\n"))
  }
  
  # Data files saved
  processed_files <- list.files(file.path(data_path, "processed"), pattern = "*.rds|*.csv", full.names = FALSE)
  cat(paste("\nSaved", length(processed_files), "processed data files:\n"))
  for(file in head(processed_files, 10)) {  # Show first 10 to avoid clutter
    cat(paste("  -", file, "\n"))
  }
  if(length(processed_files) > 10) {
    cat(paste("  ... and", length(processed_files) - 10, "more files\n"))
  }
  
  # Key Findings Summary
  cat("\nKEY FINDINGS\n")
  cat("============\n")
  cat("1. DATASET CHARACTERISTICS:\n")
  cat("   - Large-scale analysis of GTEx whole-slide image features\n")
  cat("   - Comprehensive coverage of human tissue types\n")
  cat("   - Quality metrics available for substantial subset\n\n")
  
  cat("2. TISSUE HETEROGENEITY:\n")
  cat("   - Clear tissue-specific clustering in UMAP analysis\n")
  cat("   - Variable silhouette scores indicate different tissue separability\n")
  cat("   - Feature importance varies significantly across tissue types\n\n")
  
  cat("3. QUALITY PREDICTION MODELS:\n")
  cat("   - Tissue-specific models show heterogeneous performance\n")
  cat("   - Some tissues highly predictable, others more challenging\n")
  cat("   - Cross-validation demonstrates model robustness\n\n")
  
  cat("4. FEATURE ANALYSIS:\n")
  cat("   - Statistical feature categories show different importance patterns\n")
  cat("   - Partial overlap between RIN and autolysis predictive features\n")
  cat("   - Feature consistency varies across tissues\n\n")
  
  cat("5. BIOLOGICAL INSIGHTS:\n")
  cat("   - Tissue type is strongest predictor of quality variance\n")
  cat("   - Age and sex show modest but significant effects\n")
  cat("   - Hardy scale (death circumstances) influences tissue quality\n\n")
  
  # Technical Details
  cat("TECHNICAL DETAILS\n")
  cat("=================\n")
  cat("Analysis Pipeline Components:\n")
  cat("  1. Data Integration and Quality Control\n")
  cat("  2. Demographic and Tissue Distribution Analysis\n")
  cat("  3. Principal Component Analysis (PCA)\n")
  cat("  4. Uniform Manifold Approximation and Projection (UMAP)\n")
  cat("  5. Silhouette Analysis for Cluster Validation\n")
  cat("  6. Quality Metrics Distribution Analysis\n")
  cat("  7. Analysis of Variance (ANOVA) for Demographic Effects\n")
  cat("  8. Correlation Analysis Between Quality Metrics\n")
  cat("  9. Tissue-Specific Predictive Modeling (Lasso Regression)\n")
  cat(" 10. Cross-Validation and Model Stability Assessment\n")
  cat(" 11. Feature Importance and Consistency Analysis\n")
  cat(" 12. Residual Analysis and Model Diagnostics\n")
  cat(" 13. Comparative Analysis Across Tissue Types\n\n")
  
  cat("Statistical Methods:\n")
  cat("  - 5-fold cross-validation for model training\n")
  cat("  - Lasso regularization for feature selection\n")
  cat("  - Spearman correlation for non-parametric associations\n")
  cat("  - ANOVA for variance decomposition\n")
  cat("  - Silhouette analysis for cluster quality assessment\n\n")
  
  # Footer
  cat("================================================================================\n")
  cat("                              END OF REPORT                                     \n")
  cat("================================================================================\n")
  cat(paste("Report completed:", Sys.time(), "\n"))
  cat("For questions or additional analysis, contact the analysis team.\n")
  
  sink()
  
  message("Comprehensive report generated successfully!")
  message("Report saved to: ", report_path)
  
  return(report_path)
}

# Execute the comprehensive report
start_time <- Sys.time()  # Track analysis duration
comprehensive_report_path <- generate_comprehensive_report()

#—————————————————————————— # 15. Final Report #——————————————————————————

# Generate comprehensive report
report_path <- file.path(data_path, "processed/tissue_modeling_report.txt")
sink(report_path)

cat("GTEx TISSUE-LEVEL MODELING REPORT\n")
cat("=================================\n\n")

cat("SUMMARY:\n")
cat(paste("Analysis Date:", Sys.time(), "\n"))
cat(paste("Total Tissues Analyzed:", length(unique(merged_gtex$tissue_main)), "\n"))
cat(paste("Total Samples:", nrow(merged_gtex), "\n\n"))

cat("MODELING RESULTS:\n")

# RIN Models
if("SMRIN" %in% names(all_results)) {
  rin_models <- all_results[["SMRIN"]]
  rin_r_values <- sapply(rin_models, function(x) {
    if(is.null(x)) return(NA)
    return(x$performance$pooled_r)
  # return(sqrt(x$performance$pooled_r2)) # use when required for R²
  })
  rin_r_values <- rin_r_values[!is.na(rin_r_values)]
  
  cat("\nRIN Score Prediction:\n")
  cat(paste("  Successful models:", length(rin_r_values), "\n"))
  cat(paste("  Mean R:", round(mean(rin_r_values), 3), "\n"))
  cat(paste("  Median R:", round(median(rin_r_values), 3), "\n"))
  cat(paste("  Strong correlations (R > 0.5):", sum(rin_r_values > 0.5), "\n"))
}

# Autolysis Models
if("SMATSSCR" %in% names(all_results)) {
  auto_models <- all_results[["SMATSSCR"]]
  auto_r_values <- sapply(auto_models, function(x) {
    if(is.null(x)) return(NA)
    return(sqrt(x$performance$pooled_r2))
  })
  auto_r_values <- auto_r_values[!is.na(auto_r_values)]
  
  cat("\nAutolysis Score Prediction:\n")
  cat(paste("  Successful models:", length(auto_r_values), "\n"))
  cat(paste("  Mean R:", round(mean(auto_r_values), 3), "\n"))
  cat(paste("  Median R:", round(median(auto_r_values), 3), "\n"))
  cat(paste("  Strong correlations (R > 0.5):", sum(auto_r_values > 0.5), "\n"))
}

cat("\nFEATURE ANALYSIS:\n")
cat(paste("  Common features between models:", length(common_features), "\n"))
cat(paste("  RIN-specific features:", length(rin_only_features), "\n"))
cat(paste("  Autolysis-specific features:", length(auto_only_features), "\n"))

cat("\nKEY FINDINGS:\n")
cat("1. Tissue-specific models show heterogeneous predictability\n")
cat("2. Feature importance varies across tissues and quality metrics\n")
cat("3. Cross-validation demonstrates model robustness\n")

cat("\n=================================\n")
cat("Report generated on:", as.character(Sys.time()), "\n")

sink()

message("Analysis complete! Report saved to:", report_path)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyBHVEV4IFRpc3N1ZS1MZXZlbCBNb2RlbGluZyBQaXBlbGluZQojIFB1cnBvc2U6IFRpc3N1ZS1zcGVjaWZpYyBxdWFsaXR5IHByZWRpY3Rpb24gdXNpbmcgTGFzc28gcmVncmVzc2lvbgojPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDEuIFNldHVwCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBge3J9CiMgTG9hZCBkYXRhIG9uY2UKaWYoIWV4aXN0cygibWVyZ2VkX2d0ZXgiKSkgewogIG1lcmdlZF9ndGV4IDwtIHJlYWRSRFMoZmlsZS5wYXRoKGRhdGFfcGF0aCwgInByb2Nlc3NlZC9tZXJnZWRfZ3RleC5yZHMiKSkKfQpgYGAKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAyLiBXaXRoaW4tR3JvdXAgdnMgQmV0d2Vlbi1Hcm91cCBBbmFseXNpcyBmb3IgU2luZ2xlIFRpc3N1ZQojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmBgYHtyfQojIEFuYWx5emUgYSBzcGVjaWZpYyB0aXNzdWUKdGlzc3VlX25hbWUgPC0gIkVzb3BoYWd1cyIKdGlzc3VlX2RhdGEgPC0gbWVyZ2VkX2d0ZXhbdGlzc3VlX21haW4gPT0gdGlzc3VlX25hbWVdCnN1YnR5cGVzIDwtIHVuaXF1ZSh0aXNzdWVfZGF0YSR0aXNzdWVfc3ViKQoKaWYobGVuZ3RoKHN1YnR5cGVzKSA+IDEgJiYgbnJvdyh0aXNzdWVfZGF0YSkgPj0gMTApIHsKICAjIEV4dHJhY3QgYW5kIHNjYWxlIGZlYXR1cmVzCiAgZmVhdHVyZV9jb2xzIDwtIGdyZXAoIl5mZWF0dXJlXyIsIG5hbWVzKHRpc3N1ZV9kYXRhKSwgdmFsdWUgPSBUUlVFKQogIGZlYXR1cmVfbWF0cml4IDwtIHNjYWxlKGFzLm1hdHJpeCh0aXNzdWVfZGF0YVssIC4uZmVhdHVyZV9jb2xzXSkpCiAgCiAgIyBDYWxjdWxhdGUgZGlzdGFuY2UgbWF0cml4CiAgZGlzdF9tYXRyaXggPC0gZGlzdChmZWF0dXJlX21hdHJpeCkKICAKICAjIENhbGN1bGF0ZSB3aXRoaW4tZ3JvdXAgYW5kIGJldHdlZW4tZ3JvdXAgZGlzdGFuY2VzCiAgd2l0aGluX2Rpc3RhbmNlcyA8LSBsaXN0KCkKICBiZXR3ZWVuX2Rpc3RhbmNlcyA8LSBsaXN0KCkKICAKICBmb3Ioc3VidHlwZSBpbiBzdWJ0eXBlcykgewogICAgc3VidHlwZV9pbmRpY2VzIDwtIHdoaWNoKHRpc3N1ZV9kYXRhJHRpc3N1ZV9zdWIgPT0gc3VidHlwZSkKICAgIAogICAgaWYobGVuZ3RoKHN1YnR5cGVfaW5kaWNlcykgPiAxKSB7CiAgICAgICMgV2l0aGluLWdyb3VwIGRpc3RhbmNlcwogICAgICBzdWJ0eXBlX2Rpc3QgPC0gYXMubWF0cml4KGRpc3RfbWF0cml4KVtzdWJ0eXBlX2luZGljZXMsIHN1YnR5cGVfaW5kaWNlc10KICAgICAgd2l0aGluX2Rpc3RhbmNlc1tbc3VidHlwZV1dIDwtIHN1YnR5cGVfZGlzdFt1cHBlci50cmkoc3VidHlwZV9kaXN0KV0KICAgICAgCiAgICAgICMgQmV0d2Vlbi1ncm91cCBkaXN0YW5jZXMKICAgICAgb3RoZXJfaW5kaWNlcyA8LSB3aGljaCh0aXNzdWVfZGF0YSR0aXNzdWVfc3ViICE9IHN1YnR5cGUpCiAgICAgIGlmKGxlbmd0aChvdGhlcl9pbmRpY2VzKSA+IDApIHsKICAgICAgICBiZXR3ZWVuX2Rpc3RzIDwtIGFzLm1hdHJpeChkaXN0X21hdHJpeClbc3VidHlwZV9pbmRpY2VzLCBvdGhlcl9pbmRpY2VzXQogICAgICAgIGJldHdlZW5fZGlzdGFuY2VzW1tzdWJ0eXBlXV0gPC0gYXMudmVjdG9yKGJldHdlZW5fZGlzdHMpCiAgICAgIH0KICAgIH0KICB9CiAgCiAgIyBDYWxjdWxhdGUgc3VtbWFyeSBzdGF0aXN0aWNzCiAgYWxsX3dpdGhpbiA8LSB1bmxpc3Qod2l0aGluX2Rpc3RhbmNlcykKICBhbGxfYmV0d2VlbiA8LSB1bmxpc3QoYmV0d2Vlbl9kaXN0YW5jZXMpCiAgCiAgc2ltaWxhcml0eV9yZXN1bHRzIDwtIGxpc3QoCiAgICB0aXNzdWUgPSB0aXNzdWVfbmFtZSwKICAgIHN1YnR5cGVzID0gc3VidHlwZXMsCiAgICBzYW1wbGVzX3Blcl9zdWJ0eXBlID0gdGFibGUodGlzc3VlX2RhdGEkdGlzc3VlX3N1YiksCiAgICBzdW1tYXJ5ID0gZGF0YS5mcmFtZSgKICAgICAgY29tcGFyaXNvbiA9IGMoIldpdGhpbiBTdWJ0eXBlIiwgIkJldHdlZW4gU3VidHlwZXMiKSwKICAgICAgbWVhbl9kaXN0YW5jZSA9IGMobWVhbihhbGxfd2l0aGluKSwgbWVhbihhbGxfYmV0d2VlbikpLAogICAgICBtZWRpYW5fZGlzdGFuY2UgPSBjKG1lZGlhbihhbGxfd2l0aGluKSwgbWVkaWFuKGFsbF9iZXR3ZWVuKSkKICAgICksCiAgICByYXRpbyA9IG1lYW4oYWxsX2JldHdlZW4pIC8gbWVhbihhbGxfd2l0aGluKQogICkKICAKICAjIFBFUk1BTk9WQSB0ZXN0CiAgcGVybV90ZXN0IDwtIGFkb25pczIoZGlzdF9tYXRyaXggfiB0aXNzdWVfc3ViLCBkYXRhID0gdGlzc3VlX2RhdGEpCiAgc3RhdGlzdGljYWxfcmVzdWx0cyA8LSBsaXN0KAogICAgdGlzc3VlID0gdGlzc3VlX25hbWUsCiAgICBwZXJtYW5vdmEgPSBwZXJtX3Rlc3QsCiAgICByX3NxdWFyZWQgPSBwZXJtX3Rlc3QkUjJbMV0sCiAgICBwX3ZhbHVlID0gcGVybV90ZXN0JGBQcig+RilgWzFdLAogICAgc2lnbmlmaWNhbnQgPSBwZXJtX3Rlc3QkYFByKD5GKWBbMV0gPCAwLjA1CiAgKQogIAogICMgQ3JlYXRlIHZpc3VhbGl6YXRpb25zCiAgcGNhX3Jlc3VsdCA8LSBwcmNvbXAoZmVhdHVyZV9tYXRyaXgpCiAgcGNhX3Bsb3QgPC0gZ2dwbG90KGRhdGEuZnJhbWUoUEMxID0gcGNhX3Jlc3VsdCR4WywxXSwgUEMyID0gcGNhX3Jlc3VsdCR4WywyXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3VidHlwZSA9IHRpc3N1ZV9kYXRhJHRpc3N1ZV9zdWIpLCAKICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBQQzEsIHkgPSBQQzIsIGNvbG9yID0gU3VidHlwZSkpICsKICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjcsIHNpemUgPSAzKSArCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgbGFicyh0aXRsZSA9IHBhc3RlKCJQQ0Egb2YiLCB0aXNzdWVfbmFtZSwgImJ5IFN1YnR5cGUiKSkKICAKICBnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCBwYXN0ZTAodGlzc3VlX25hbWUsICJfc3VidHlwZV9wY2EucGRmIikpLCAKICAgICAgICAgcGNhX3Bsb3QsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkKICAKICAjIFVNQVAgdmlzdWFsaXphdGlvbgogIHNldC5zZWVkKDEyMykKICB1bWFwX3Jlc3VsdCA8LSB1d290Ojp1bWFwKGZlYXR1cmVfbWF0cml4LCBuX25laWdoYm9ycyA9IG1pbigxNSwgbnJvdyhmZWF0dXJlX21hdHJpeCktMSkpCiAgCiAgdW1hcF9wbG90IDwtIGdncGxvdChkYXRhLmZyYW1lKFVNQVAxID0gdW1hcF9yZXN1bHRbLDFdLCBVTUFQMiA9IHVtYXBfcmVzdWx0WywyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3VidHlwZSA9IHRpc3N1ZV9kYXRhJHRpc3N1ZV9zdWIpLAogICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFVNQVAxLCB5ID0gVU1BUDIsIGNvbG9yID0gU3VidHlwZSkpICsKICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjcsIHNpemUgPSAzKSArCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgbGFicyh0aXRsZSA9IHBhc3RlKCJVTUFQIG9mIiwgdGlzc3VlX25hbWUsICJieSBTdWJ0eXBlIikpCiAgCiAgZ2dzYXZlKGZpbGUucGF0aChvdXRwdXRfcGF0aCwgcGFzdGUwKHRpc3N1ZV9uYW1lLCAiX3N1YnR5cGVfdW1hcC5wZGYiKSksIAogICAgICAgICB1bWFwX3Bsb3QsIHdpZHRoID0gMTAsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkKICAKICAjIFNhdmUgcmVzdWx0cwogIHNhdmVSRFMoc2ltaWxhcml0eV9yZXN1bHRzLCBmaWxlLnBhdGgocmVzdWx0c19wYXRoLCBwYXN0ZTAodGlzc3VlX25hbWUsICJfZGlzdGFuY2VfYW5hbHlzaXMucmRzIikpKQogIHNhdmVSRFMoc3RhdGlzdGljYWxfcmVzdWx0cywgZmlsZS5wYXRoKHJlc3VsdHNfcGF0aCwgcGFzdGUwKHRpc3N1ZV9uYW1lLCAiX3N0YXRpc3RpY2FsX3Rlc3RzLnJkcyIpKSkKfQpgYGAKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAzLiBUaXNzdWUtTGV2ZWwgTGFzc28gTW9kZWxpbmcKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpgYGB7cn0KIyBTZXQgcGFyYW1ldGVycwprX2ZvbGRzIDwtIDUKdGFyZ2V0cyA8LSBjKCJTTVJJTiIsICJTTUFUU1NDUiIpCnRpc3N1ZXMgPC0gdW5pcXVlKG1lcmdlZF9ndGV4JHRpc3N1ZV9tYWluKQphbGxfcmVzdWx0cyA8LSBsaXN0KCkKCiMgUHJvY2VzcyBlYWNoIHRhcmdldApmb3IodGFyZ2V0IGluIHRhcmdldHMpIHsKICB0YXJnZXRfcmVzdWx0cyA8LSBsaXN0KCkKICBtZXNzYWdlKHBhc3RlKCJcblByb2Nlc3NpbmcgdGFyZ2V0OiIsIHRhcmdldCkpCiAgCiAgIyBQcm9jZXNzIGVhY2ggdGlzc3VlCiAgZm9yKHRpc3N1ZV9uYW1lIGluIHRpc3N1ZXMpIHsKICAgIG1lc3NhZ2UocGFzdGUoIlxuQW5hbHl6aW5nIiwgdGlzc3VlX25hbWUsICJmb3IiLCB0YXJnZXQpKQogICAgCiAgICAjIEV4dHJhY3QgYW5kIGZpbHRlciBkYXRhCiAgICB0aXNzdWVfZGF0YSA8LSBtZXJnZWRfZ3RleFt0aXNzdWVfbWFpbiA9PSB0aXNzdWVfbmFtZV0KICAgIHRpc3N1ZV9kYXRhIDwtIHRpc3N1ZV9kYXRhWyFpcy5uYSh0aXNzdWVfZGF0YVtbdGFyZ2V0XV0pXQogICAgCiAgICBpZihucm93KHRpc3N1ZV9kYXRhKSA8IDEwKSB7CiAgICAgIG1lc3NhZ2UocGFzdGUoIkluc3VmZmljaWVudCBzYW1wbGVzIGZvciIsIHRpc3N1ZV9uYW1lKSkKICAgICAgbmV4dAogICAgfQogICAgCiAgICAjIEZlYXR1cmUgc2VsZWN0aW9uCiAgICBmZWF0dXJlX2NvbHMgPC0gZ3JlcCgiXmZlYXR1cmVfIiwgbmFtZXModGlzc3VlX2RhdGEpLCB2YWx1ZSA9IFRSVUUpCiAgICBmZWF0dXJlX3ZhcnMgPC0gYXBwbHkodGlzc3VlX2RhdGFbLCAuLmZlYXR1cmVfY29sc10sIDIsIHZhcikKICAgIHZhbGlkX2ZlYXR1cmVzIDwtIGZlYXR1cmVfY29sc1tmZWF0dXJlX3ZhcnMgPiAxZS0xMF0KICAgIAogICAgIyBDcmVhdGUgY3Jvc3MtdmFsaWRhdGlvbiBmb2xkcwogICAgc2V0LnNlZWQoMTIzKQogICAgZm9sZHMgPC0gY3JlYXRlRm9sZHModGlzc3VlX2RhdGFbW3RhcmdldF1dLCBrID0ga19mb2xkcykKICAgIAogICAgIyBDcm9zcy12YWxpZGF0aW9uCiAgICBmb2xkX3Jlc3VsdHMgPC0gbGlzdCgpCiAgICBhbGxfZm9sZF9wcmVkaWN0aW9ucyA8LSBkYXRhLmZyYW1lKCkKICAgIAogICAgZm9yKGkgaW4gMTprX2ZvbGRzKSB7CiAgICAgICMgU3BsaXQgZGF0YQogICAgICB0ZXN0X2luZGljZXMgPC0gZm9sZHNbW2ldXQogICAgICB0cmFpbl9kYXRhIDwtIHRpc3N1ZV9kYXRhWy10ZXN0X2luZGljZXNdCiAgICAgIHRlc3RfZGF0YSA8LSB0aXNzdWVfZGF0YVt0ZXN0X2luZGljZXNdCiAgICAgIAogICAgICAjIEZlYXR1cmUgc2VsZWN0aW9uIC0gdG9wIDUlIGNvcnJlbGF0ZWQKICAgICAgZmVhdHVyZV9jb3JyZWxhdGlvbnMgPC0gc2FwcGx5KHZhbGlkX2ZlYXR1cmVzLCBmdW5jdGlvbihjb2wpIHsKICAgICAgICBjb3IodHJhaW5fZGF0YVtbY29sXV0sIHRyYWluX2RhdGFbW3RhcmdldF1dLCB1c2UgPSAiY29tcGxldGUub2JzIikKICAgICAgfSkKICAgICAgCiAgICAgIHRvcF9uX2ZlYXR1cmVzIDwtIGNlaWxpbmcobGVuZ3RoKHZhbGlkX2ZlYXR1cmVzKSAqIDAuMDUpCiAgICAgIHRvcF9mZWF0dXJlcyA8LSBuYW1lcyhzb3J0KGFicyhmZWF0dXJlX2NvcnJlbGF0aW9ucyksIGRlY3JlYXNpbmcgPSBUUlVFKSlbMTp0b3Bfbl9mZWF0dXJlc10KICAgICAgCiAgICAgICMgUHJlcGFyZSBtYXRyaWNlcwogICAgICB4X3RyYWluIDwtIGFzLm1hdHJpeCh0cmFpbl9kYXRhWywgLi50b3BfZmVhdHVyZXNdKQogICAgICB5X3RyYWluIDwtIHRyYWluX2RhdGFbW3RhcmdldF1dCiAgICAgIHhfdHJhaW5fc2NhbGVkIDwtIHNjYWxlKHhfdHJhaW4pCiAgICAgIHhfbWVhbiA8LSBhdHRyKHhfdHJhaW5fc2NhbGVkLCAic2NhbGVkOmNlbnRlciIpCiAgICAgIHhfc2QgPC0gYXR0cih4X3RyYWluX3NjYWxlZCwgInNjYWxlZDpzY2FsZSIpCiAgICAgIAogICAgICAjIFRyYWluIG1vZGVsCiAgICAgIGN2X2ZpdCA8LSBjdi5nbG1uZXQoeF90cmFpbl9zY2FsZWQsIHlfdHJhaW4sIGFscGhhID0gMSkKICAgICAgYmVzdF9sYW1iZGEgPC0gY3ZfZml0JGxhbWJkYS5taW4KICAgICAgbGFzc29fbW9kZWwgPC0gZ2xtbmV0KHhfdHJhaW5fc2NhbGVkLCB5X3RyYWluLCBhbHBoYSA9IDEsIGxhbWJkYSA9IGJlc3RfbGFtYmRhKQogICAgICAKICAgICAgIyBNYWtlIHByZWRpY3Rpb25zCiAgICAgIHhfdGVzdCA8LSBhcy5tYXRyaXgodGVzdF9kYXRhWywgLi50b3BfZmVhdHVyZXNdKQogICAgICB4X3Rlc3Rfc2NhbGVkIDwtIHNjYWxlKHhfdGVzdCwgY2VudGVyID0geF9tZWFuLCBzY2FsZSA9IHhfc2QpCiAgICAgIHByZWRpY3Rpb25zIDwtIHByZWRpY3QobGFzc29fbW9kZWwsIHhfdGVzdF9zY2FsZWQsIHMgPSBiZXN0X2xhbWJkYSkKICAgICAgCiAgICAgICMgU3RvcmUgcHJlZGljdGlvbnMKICAgICAgZm9sZF9wcmVkaWN0aW9ucyA8LSBkYXRhLmZyYW1lKAogICAgICAgIGZvbGQgPSBpLAogICAgICAgIHNhbXBsZV9pZCA9IHRlc3RfZGF0YSRzYW1wbGVfaWQsCiAgICAgICAgYWN0dWFsID0gdGVzdF9kYXRhW1t0YXJnZXRdXSwKICAgICAgICBwcmVkaWN0ZWQgPSBhcy5udW1lcmljKHByZWRpY3Rpb25zKQogICAgICApCiAgICAgIGFsbF9mb2xkX3ByZWRpY3Rpb25zIDwtIHJiaW5kKGFsbF9mb2xkX3ByZWRpY3Rpb25zLCBmb2xkX3ByZWRpY3Rpb25zKQogICAgICAKICAgICAgIyBDYWxjdWxhdGUgbWV0cmljcwogICAgICBybXNlIDwtIHNxcnQobWVhbigocHJlZGljdGlvbnMgLSB0ZXN0X2RhdGFbW3RhcmdldF1dKV4yKSkKICAgICAgcl92YWx1ZSA8LSBjb3IocHJlZGljdGlvbnMsIHRlc3RfZGF0YVtbdGFyZ2V0XV0pIAogICAgICByMiA8LSBjb3IocHJlZGljdGlvbnMsIHRlc3RfZGF0YVtbdGFyZ2V0XV0pXjIKICAgICAgbWFlIDwtIG1lYW4oYWJzKHByZWRpY3Rpb25zIC0gdGVzdF9kYXRhW1t0YXJnZXRdXSkpCiAgICAgIAogICAgICBmb2xkX3Jlc3VsdHNbW2ldXSA8LSBsaXN0KAogICAgICAgIGZvbGQgPSBpLAogICAgICAgIHJtc2UgPSBybXNlLAogICAgICAgIHIgPSByX3ZhbHVlLAogICAgICAgIHIyID0gcjIsCiAgICAgICAgbWFlID0gbWFlLAogICAgICAgIGxhbWJkYSA9IGJlc3RfbGFtYmRhLAogICAgICAgIG1vZGVsID0gbGFzc29fbW9kZWwsCiAgICAgICAgZmVhdHVyZXMgPSB0b3BfZmVhdHVyZXMKICAgICAgKQogICAgfQogICAgCiAgICAjIENhbGN1bGF0ZSBvdmVyYWxsIHBlcmZvcm1hbmNlCiAgICBwb29sZWRfcm1zZSA8LSBzcXJ0KG1lYW4oKGFsbF9mb2xkX3ByZWRpY3Rpb25zJHByZWRpY3RlZCAtIGFsbF9mb2xkX3ByZWRpY3Rpb25zJGFjdHVhbCleMikpCiAgICBwb29sZWRfciA8LSBjb3IoYWxsX2ZvbGRfcHJlZGljdGlvbnMkcHJlZGljdGVkLCBhbGxfZm9sZF9wcmVkaWN0aW9ucyRhY3R1YWwpICAjIDwtLSBSLCBub3QgUsKyCiAgICBwb29sZWRfcjIgPC0gY29yKGFsbF9mb2xkX3ByZWRpY3Rpb25zJHByZWRpY3RlZCwgYWxsX2ZvbGRfcHJlZGljdGlvbnMkYWN0dWFsKV4yCiAgICBwb29sZWRfbWFlIDwtIG1lYW4oYWJzKGFsbF9mb2xkX3ByZWRpY3Rpb25zJHByZWRpY3RlZCAtIGFsbF9mb2xkX3ByZWRpY3Rpb25zJGFjdHVhbCkpCiAgICAKICAgICMgQ291bnQgZmVhdHVyZSBzZWxlY3Rpb24gZnJlcXVlbmN5CiAgICBhbGxfc2VsZWN0ZWRfZmVhdHVyZXMgPC0gdW5saXN0KGxhcHBseShmb2xkX3Jlc3VsdHMsIGZ1bmN0aW9uKHgpIHgkZmVhdHVyZXMpKQogICAgZmVhdHVyZV9jb3VudHMgPC0gdGFibGUoYWxsX3NlbGVjdGVkX2ZlYXR1cmVzKQogICAgY29uc2lzdGVudF9mZWF0dXJlcyA8LSBuYW1lcyhmZWF0dXJlX2NvdW50c1tmZWF0dXJlX2NvdW50cyA+PSAzXSkKICAgIAogICAgIyBTdG9yZSByZXN1bHRzCiAgICB0aXNzdWVfbW9kZWxfcmVzdWx0IDwtIGxpc3QoCiAgICAgIHRpc3N1ZSA9IHRpc3N1ZV9uYW1lLAogICAgICB0YXJnZXQgPSB0YXJnZXQsCiAgICAgIHBlcmZvcm1hbmNlID0gbGlzdCgKICAgICAgICBwb29sZWRfcm1zZSA9IHBvb2xlZF9ybXNlLAogICAgICAgIHBvb2xlZF9yID0gcG9vbGVkX3IsCiAgICAgICAgcG9vbGVkX3IyID0gcG9vbGVkX3IyLAogICAgICAgIHBvb2xlZF9tYWUgPSBwb29sZWRfbWFlCiAgICAgICksCiAgICAgIGZvbGRfcmVzdWx0cyA9IGZvbGRfcmVzdWx0cywKICAgICAgZmVhdHVyZV9jb3VudHMgPSBmZWF0dXJlX2NvdW50cywKICAgICAgY29uc2lzdGVudF9mZWF0dXJlcyA9IGNvbnNpc3RlbnRfZmVhdHVyZXMsCiAgICAgIHNhbXBsZV9zaXplID0gbnJvdyh0aXNzdWVfZGF0YSksCiAgICAgIGFsbF9wcmVkaWN0aW9ucyA9IGFsbF9mb2xkX3ByZWRpY3Rpb25zCiAgICApCiAgICAKICAgIHRhcmdldF9yZXN1bHRzW1t0aXNzdWVfbmFtZV1dIDwtIHRpc3N1ZV9tb2RlbF9yZXN1bHQKICAgIAogICAgIyBDcmVhdGUgdmlzdWFsaXphdGlvbiBmb3IgZ29vZCBtb2RlbHMgKHVzaW5nIFIgPiAwLjU1IHdoaWNoIGlzIGFwcHJveGltYXRlbHkgUsKyID4gMC4zKQogICMgaWYocG9vbGVkX3IyID4gMC4zKSB7ICAjIHVzZSBpZiBSwrIgaXMgcmVxdWlyZWQKICAgIGlmKGFicyhwb29sZWRfcikgPiAwLjU1KSB7CiAgICAgIHNjYXR0ZXJfcGxvdCA8LSBnZ3Bsb3QoYWxsX2ZvbGRfcHJlZGljdGlvbnMsIGFlcyh4ID0gYWN0dWFsLCB5ID0gcHJlZGljdGVkKSkgKwogICAgICAgIGdlb21fcG9pbnQoYWxwaGEgPSAwLjYsIGNvbG9yID0gImRhcmtibHVlIiwgc2l6ZSA9IDIpICsKICAgICAgICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEsIGNvbG9yID0gInJlZCIsIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICAgICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IFRSVUUsIGNvbG9yID0gImJsdWUiKSArCiAgICAgICAgbGFicygKICAgICAgICAgIHRpdGxlID0gcGFzdGUodGlzc3VlX25hbWUsICItIiwgdGFyZ2V0LCAiUHJlZGljdGlvbiIpLAogICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZSgiUiA9Iiwgcm91bmQocG9vbGVkX3IsIDMpLCAiUk1TRSA9Iiwgcm91bmQocG9vbGVkX3Jtc2UsIDMpKSwKICAgICAgICAjIHN1YnRpdGxlID0gcGFzdGUoIlLCsiA9Iiwgcm91bmQocG9vbGVkX3IyLCAzKSwgIlJNU0UgPSIsIHJvdW5kKHBvb2xlZF9ybXNlLCAzKSksICMgdXNlIGlmIFLCsiBpcyByZXF1aXJlZAogICAgICAgICAgeCA9IHBhc3RlKCJBY3R1YWwiLCB0YXJnZXQpLAogICAgICAgICAgeSA9IHBhc3RlKCJQcmVkaWN0ZWQiLCB0YXJnZXQpCiAgICAgICAgKSArCiAgICAgICAgdGhlbWVfbWluaW1hbCgpCiAgICAgIAogICAgICBnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCBwYXN0ZTAodGlzc3VlX25hbWUsICJfIiwgdGFyZ2V0LCAiX3NjYXR0ZXIucGRmIikpLCAKICAgICAgICAgICAgIHNjYXR0ZXJfcGxvdCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNiwgZHBpID0gMzAwKQogICAgfQogIH0KICAKICBhbGxfcmVzdWx0c1tbdGFyZ2V0XV0gPC0gdGFyZ2V0X3Jlc3VsdHMKICBzYXZlUkRTKHRhcmdldF9yZXN1bHRzLCBmaWxlLnBhdGgocmVzdWx0c19wYXRoLCBwYXN0ZTAodGFyZ2V0LCAiX21vZGVsX3Jlc3VsdHMucmRzIikpKQp9CgpzYXZlUkRTKGFsbF9yZXN1bHRzLCBmaWxlLnBhdGgocmVzdWx0c19wYXRoLCAiYWxsX3Rpc3N1ZV9tb2RlbHNfY29tcGxldGUucmRzIikpCmBgYAoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDQuIE1vZGVsIFBlcmZvcm1hbmNlIFN1bW1hcnkKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpgYGB7cn0KIyBDcmVhdGUgcGVyZm9ybWFuY2Ugc3VtbWFyeQpjcmVhdGVfcGVyZm9ybWFuY2Vfc3VtbWFyeSA8LSBmdW5jdGlvbihyZXN1bHRzLCB0YXJnZXRfbmFtZSkgewogIHN1bW1hcnlfZGYgPC0gZGF0YS5mcmFtZSgpCiAgCiAgZm9yKHRpc3N1ZSBpbiBuYW1lcyhyZXN1bHRzKSkgewogICAgdGlzc3VlX3Jlc3VsdCA8LSByZXN1bHRzW1t0aXNzdWVdXQogICAgaWYoaXMubnVsbCh0aXNzdWVfcmVzdWx0KSkgbmV4dAogICAgCiAgICByb3cgPC0gZGF0YS5mcmFtZSgKICAgICAgVGlzc3VlID0gdGlzc3VlLAogICAgICBTYW1wbGVzID0gdGlzc3VlX3Jlc3VsdCRzYW1wbGVfc2l6ZSwKICAgICAgUk1TRSA9IHJvdW5kKHRpc3N1ZV9yZXN1bHQkcGVyZm9ybWFuY2UkcG9vbGVkX3Jtc2UsIDMpLAogICAgICBSID0gcm91bmQodGlzc3VlX3Jlc3VsdCRwZXJmb3JtYW5jZSRwb29sZWRfciwgMyksCiAgICAjIFJfc3F1YXJlZCA9IHJvdW5kKHRpc3N1ZV9yZXN1bHQkcGVyZm9ybWFuY2UkcG9vbGVkX3IyLCAzKSwgIyB1c2UgaWYgUsKyIGlzIHJlcXVpcmVkCiAgICAgIE1BRSA9IHJvdW5kKHRpc3N1ZV9yZXN1bHQkcGVyZm9ybWFuY2UkcG9vbGVkX21hZSwgMyksCiAgICAgIENvbnNpc3RlbnRfRmVhdHVyZXMgPSBsZW5ndGgodGlzc3VlX3Jlc3VsdCRjb25zaXN0ZW50X2ZlYXR1cmVzKQogICAgKQogICAgc3VtbWFyeV9kZiA8LSByYmluZChzdW1tYXJ5X2RmLCByb3cpCiAgfQogIAogIHN1bW1hcnlfZGYgPC0gc3VtbWFyeV9kZltvcmRlcihhYnMoc3VtbWFyeV9kZiRSKSwgZGVjcmVhc2luZyA9IFRSVUUpLF0gICMgU29ydCBieSBhYnNvbHV0ZSBSCiAgc3VtbWFyeV9kZiRQZXJmb3JtYW5jZV9DYXRlZ29yeSA8LSBjdXQoYWJzKHN1bW1hcnlfZGYkUiksICAjIFVzZSBhYnNvbHV0ZSBSIGZvciBjYXRlZ29yaWVzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygtSW5mLCAwLjMxNiwgMC41NDgsIDAuNzA3LCBJbmYpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJQb29yIiwgIk1vZGVyYXRlIiwgIkdvb2QiLCAiRXhjZWxsZW50IikpICAKCiMgc3VtbWFyeV9kZiA8LSBzdW1tYXJ5X2RmW29yZGVyKHN1bW1hcnlfZGYkUl9zcXVhcmVkLCBkZWNyZWFzaW5nID0gVFJVRSksXSAjIFNvcnQgYnkgUsKyCiMgc3VtbWFyeV9kZiRQZXJmb3JtYW5jZV9DYXRlZ29yeSA8LSBjdXQoc3VtbWFyeV9kZiRSX3NxdWFyZWQsICMgVXNlIFLCsiBmb3IgY2F0ZWdvcmllcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyAgYnJlYWtzID0gYygtSW5mLCAwLjEsIDAuMywgMC41LCBJbmYpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgIGxhYmVscyA9IGMoIlBvb3IiLCAiTW9kZXJhdGUiLCAiR29vZCIsICJFeGNlbGxlbnQiKSkKICAKICByZXR1cm4oc3VtbWFyeV9kZikKfQoKIyBDcmVhdGUgc3VtbWFyaWVzCnJpbl9zdW1tYXJ5IDwtIGNyZWF0ZV9wZXJmb3JtYW5jZV9zdW1tYXJ5KGFsbF9yZXN1bHRzW1siU01SSU4iXV0sICJSSU4gU2NvcmUiKQphdXRvbHlzaXNfc3VtbWFyeSA8LSBjcmVhdGVfcGVyZm9ybWFuY2Vfc3VtbWFyeShhbGxfcmVzdWx0c1tbIlNNQVRTU0NSIl1dLCAiQXV0b2x5c2lzIFNjb3JlIikKCiMgU2F2ZSBzdW1tYXJpZXMKd3JpdGUuY3N2KHJpbl9zdW1tYXJ5LCBmaWxlLnBhdGgocmVzdWx0c19wYXRoLCAicmluX21vZGVsX3N1bW1hcnkuY3N2IiksIHJvdy5uYW1lcyA9IEZBTFNFKQp3cml0ZS5jc3YoYXV0b2x5c2lzX3N1bW1hcnksIGZpbGUucGF0aChyZXN1bHRzX3BhdGgsICJhdXRvbHlzaXNfbW9kZWxfc3VtbWFyeS5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpCgojIENyZWF0ZSBjb21wYXJpc29uCm1vZGVsX2NvbXBhcmlzb24gPC0gbWVyZ2UoCiAgcmluX3N1bW1hcnlbLCBjKCJUaXNzdWUiLCAiUiIpXSwKICBhdXRvbHlzaXNfc3VtbWFyeVssIGMoIlRpc3N1ZSIsICJSIildLAogIGJ5ID0gIlRpc3N1ZSIsCiAgc3VmZml4ZXMgPSBjKCJfUklOIiwgIl9BdXRvIikKKQptb2RlbF9jb21wYXJpc29uJEJldHRlcl9Nb2RlbCA8LSBpZmVsc2UoYWJzKG1vZGVsX2NvbXBhcmlzb24kUl9SSU4pID4gYWJzKG1vZGVsX2NvbXBhcmlzb24kUl9BdXRvKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlJJTiIsICJBdXRvbHlzaXMiKQoKIyBDcmVhdGUgY29tcGFyaXNvbiB3aGVuIGlmIHJlcXVpcmVkIGZvciBSwrIKIyBtb2RlbF9jb21wYXJpc29uIDwtIG1lcmdlKAojICAgcmluX3N1bW1hcnlbLCBjKCJUaXNzdWUiLCAiUl9zcXVhcmVkIildLAojICAgYXV0b2x5c2lzX3N1bW1hcnlbLCBjKCJUaXNzdWUiLCAiUl9zcXVhcmVkIildLAojICAgYnkgPSAiVGlzc3VlIiwKIyAgIHN1ZmZpeGVzID0gYygiX1JJTiIsICJfQXV0byIpCiMgKQojIG1vZGVsX2NvbXBhcmlzb24kQmV0dGVyX01vZGVsIDwtIGlmZWxzZShtb2RlbF9jb21wYXJpc29uJFJfc3F1YXJlZF9SSU4gPiBtb2RlbF9jb21wYXJpc29uJFJfc3F1YXJlZF9BdXRvLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJSSU4iLCAiQXV0b2x5c2lzIikKd3JpdGUuY3N2KG1vZGVsX2NvbXBhcmlzb24sIGZpbGUucGF0aChyZXN1bHRzX3BhdGgsICJtb2RlbF9jb21wYXJpc29uLmNzdiIpLCByb3cubmFtZXMgPSBGQUxTRSkKYGBgCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgNS4gRmlndXJlIDMgLSBNb2RlbCBQZXJmb3JtYW5jZSBWaXN1YWxpemF0aW9uCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBge3J9CiMgUGFuZWwgQSAmIEI6IFBlcmZvcm1hbmNlIGJhciBwbG90cwpjcmVhdGVfaG9yaXpvbnRhbF9iYXJwbG90IDwtIGZ1bmN0aW9uKGFsbF9yZXN1bHRzLCBzY29yZV90eXBlLCBwYW5lbF9sYWJlbCkgewogIHJfdmFsdWVzIDwtIHNhcHBseShhbGxfcmVzdWx0c1tbc2NvcmVfdHlwZV1dLCBmdW5jdGlvbih4KSB7CiAgICBpZihpcy5udWxsKHgpKSByZXR1cm4oMCkKICAgIHJldHVybih4JHBlcmZvcm1hbmNlJHBvb2xlZF9yKQogICMgcmV0dXJuKHNxcnQoeCRwZXJmb3JtYW5jZSRwb29sZWRfcjIpKSAjIHVzZSB3aGVuIHJlcXVpcmVkIGZvciBSwrIKICB9KQogIAogIHBsb3RfZGF0YSA8LSBkYXRhLmZyYW1lKAogICAgVGlzc3VlID0gbmFtZXMocl92YWx1ZXMpLAogICAgUiA9IHJfdmFsdWVzCiAgKVtvcmRlcigtcl92YWx1ZXMpLCBdCiAgCiAgcGxvdF9kYXRhJFRpc3N1ZSA8LSBmYWN0b3IocGxvdF9kYXRhJFRpc3N1ZSwgbGV2ZWxzID0gcmV2KHBsb3RfZGF0YSRUaXNzdWUpKQogIAogIGlmKHNjb3JlX3R5cGUgPT0gIlNNUklOIikgewogICAgY29sb3JfZ3JhZGllbnQgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCIjRThGNUU4IiwgIiM2NmNkYWEiLCAiIzJFOEI1NyIpKSgxMDApCiAgfSBlbHNlIHsKICAgIGNvbG9yX2dyYWRpZW50IDwtIGNvbG9yUmFtcFBhbGV0dGUoYygiI0ZGRjJFNiIsICIjRkM4RDYyIiwgIiNEOTQ4MDEiKSkoMTAwKQogIH0KICAKICBiYXJfcGxvdCA8LSBnZ3Bsb3QocGxvdF9kYXRhLCBhZXMoeCA9IFIsIHkgPSBUaXNzdWUsIGZpbGwgPSBSKSkgKwogICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHdpZHRoID0gMC44KSArCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gc3ByaW50ZigiJS4zZiIsIFIpLCB4ID0gUi8yKSwgCiAgICAgICAgICAgICAgaGp1c3QgPSAwLjUsIHNpemUgPSAzLjUsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgICBzY2FsZV9maWxsX2dyYWRpZW50bihjb2xvcnMgPSBjb2xvcl9ncmFkaWVudCwgZ3VpZGUgPSAibm9uZSIpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBsYWJzKHRpdGxlID0gcGFuZWxfbGFiZWwsIHggPSAiQ29ycmVsYXRpb24gQ29lZmZpY2llbnQgKFIpIiwgeSA9ICIiKSArCiAgICB0aGVtZSgKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjAsIGZhY2UgPSAiYm9sZCIsIGhqdXN0ID0gMCksCiAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIpLAogICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMsIGZhY2UgPSAiYm9sZCIpCiAgICApCiAgCiAgcmV0dXJuKGJhcl9wbG90KQp9CgpyaW5fYmFycGxvdCA8LSBjcmVhdGVfaG9yaXpvbnRhbF9iYXJwbG90KGFsbF9yZXN1bHRzLCAiU01SSU4iLCAiQSIpCmF1dG9seXNpc19iYXJwbG90IDwtIGNyZWF0ZV9ob3Jpem9udGFsX2JhcnBsb3QoYWxsX3Jlc3VsdHMsICJTTUFUU1NDUiIsICJCIikKCiMgUGFuZWwgQzogUklOIHNjYXR0ZXIgcGxvdHMKY3JlYXRlX3NjYXR0ZXJfcGFuZWwgPC0gZnVuY3Rpb24oYWxsX3Jlc3VsdHMsIHRvcF90aXNzdWVzKSB7CiAgcGxvdHMgPC0gbGlzdCgpCiAgCiAgZm9yKGkgaW4gMTozKSB7CiAgICByZXN1bHQgPC0gYWxsX3Jlc3VsdHNbWyJTTVJJTiJdXVtbdG9wX3Rpc3N1ZXNbaV1dXQogICAgaWYoaXMubnVsbChyZXN1bHQpKSBuZXh0CiAgICAKICAgIGFsbF9wcmVkcyA8LSByZXN1bHQkYWxsX3ByZWRpY3Rpb25zCiAgICByX3ZhbHVlIDwtIHJlc3VsdCRwZXJmb3JtYW5jZSRwb29sZWRfcgogICMgcl92YWx1ZSA8LSBzcXJ0KHJlc3VsdCRwZXJmb3JtYW5jZSRwb29sZWRfcjIpICMgdXNlIHdoZW4gcmVxdWlyZWQgZm9yIFLCsgogICAgcm1zZV92YWx1ZSA8LSByZXN1bHQkcGVyZm9ybWFuY2UkcG9vbGVkX3Jtc2UKICAgIAogICAgcCA8LSBnZ3Bsb3QoYWxsX3ByZWRzLCBhZXMoeCA9IGFjdHVhbCwgeSA9IHByZWRpY3RlZCkpICsKICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNywgY29sb3IgPSAiIzY2Y2RhYSIsIHNpemUgPSAxLjUpICsKICAgICAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVFJVRSwgY29sb3IgPSAiYmx1ZSIpICsKICAgICAgZ2d0aXRsZShwYXN0ZTAodG9wX3Rpc3N1ZXNbaV0sICIgfCBSID0gIiwgcm91bmQocl92YWx1ZSwgMykpKSArCiAgICAgIHRoZW1lX21pbmltYWwoKQogICAgCiAgICBwbG90c1tbaV1dIDwtIHAKICB9CiAgCiAgcmV0dXJuKHBsb3RzKQp9CgojIEdldCB0b3AgMyB0aXNzdWVzCmFsbF9yX3JpbiA8LSBzYXBwbHkoYWxsX3Jlc3VsdHNbWyJTTVJJTiJdXSwgZnVuY3Rpb24oeCkgewogIGlmKGlzLm51bGwoeCkpIHJldHVybigwKQogIHJldHVybihhYnMoeCRwZXJmb3JtYW5jZSRwb29sZWRfcikpICAjIFVzZSBhYnNvbHV0ZSB2YWx1ZSBmb3Igc29ydGluZwojIHJldHVybihzcXJ0KHgkcGVyZm9ybWFuY2UkcG9vbGVkX3IyKSkgIyB1c2Ugd2hlbiByZXF1aXJlZCBmb3IgUsKyCn0pCnRvcF9yaW5fdGlzc3VlcyA8LSBuYW1lcyhzb3J0KGFsbF9yX3JpbiwgZGVjcmVhc2luZyA9IFRSVUUpKVsxOjNdCgpyaW5fc2NhdHRlcl9wbG90cyA8LSBjcmVhdGVfc2NhdHRlcl9wYW5lbChhbGxfcmVzdWx0cywgdG9wX3Jpbl90aXNzdWVzKQpyaW5fZ3JpZCA8LSBncmlkLmFycmFuZ2UoZ3JvYnMgPSByaW5fc2NhdHRlcl9wbG90cywgbnJvdyA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgIGJvdHRvbSA9IHRleHRHcm9iKCJBY3R1YWwgUklOIFNjb3JlIiwgZ3AgPSBncGFyKGZvbnRzaXplID0gMTgsIGZvbnRmYWNlID0gImJvbGQiKSksCiAgICAgICAgICAgICAgICAgICAgICAgIGxlZnQgPSB0ZXh0R3JvYigiUHJlZGljdGVkIFJJTiBTY29yZSIsIHJvdCA9IDkwLCBncCA9IGdwYXIoZm9udHNpemUgPSAxOCwgZm9udGZhY2UgPSAiYm9sZCIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgdG9wID0gdGV4dEdyb2IoIkMiLCB4ID0gMC4wMiwganVzdCA9ICJsZWZ0IiwgZ3AgPSBncGFyKGZvbnRzaXplID0gMTgsIGZvbnRmYWNlID0gImJvbGQiKSkpCgojIFBhbmVsIEQ6IEF1dG9seXNpcyBib3ggcGxvdHMKY3JlYXRlX2JveHBsb3RfcGFuZWwgPC0gZnVuY3Rpb24oYWxsX3Jlc3VsdHMsIHRvcF90aXNzdWVzKSB7CiAgcGxvdHMgPC0gbGlzdCgpCiAgCiAgZm9yKGkgaW4gMTozKSB7CiAgICByZXN1bHQgPC0gYWxsX3Jlc3VsdHNbWyJTTUFUU1NDUiJdXVtbdG9wX3Rpc3N1ZXNbaV1dXQogICAgaWYoaXMubnVsbChyZXN1bHQpKSBuZXh0CiAgICAKICAgIGFsbF9wcmVkcyA8LSByZXN1bHQkYWxsX3ByZWRpY3Rpb25zCiAgICBhbGxfcHJlZHMkYWN0dWFsX2ZhY3RvciA8LSBmYWN0b3IoYWxsX3ByZWRzJGFjdHVhbCwgbGV2ZWxzID0gYygwLCAxLCAyLCAzKSkKICAgIHJfdmFsdWUgPC0gc3FydChyZXN1bHQkcGVyZm9ybWFuY2UkcG9vbGVkX3IyKQogICAgcm1zZV92YWx1ZSA8LSByZXN1bHQkcGVyZm9ybWFuY2UkcG9vbGVkX3Jtc2UKICAgIAogICAgcCA8LSBnZ3Bsb3QoYWxsX3ByZWRzLCBhZXMoeCA9IGFjdHVhbF9mYWN0b3IsIHkgPSBwcmVkaWN0ZWQpKSArCiAgICAgIGdlb21fYm94cGxvdChhZXMoZmlsbCA9IGFjdHVhbF9mYWN0b3IpLCBvdXRsaWVyLnNoYXBlID0gTkEpICsKICAgICAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjIsIGhlaWdodCA9IDAsIGFscGhhID0gMC4yLCBjb2xvciA9ICIjMjE5MDhkIikgKwogICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIwIiA9ICIjZTBlMGUwIiwgIjEiID0gIiNiZGQ3ZTciLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjIiID0gIiM5ZWNhZTEiLCAiMyIgPSAiIzZiYWVkNiIpKSArCiAgICAgIGdndGl0bGUocGFzdGUwKHRvcF90aXNzdWVzW2ldLCAiIHwgUiA9ICIsIHJvdW5kKHJfdmFsdWUsIDMpKSkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCiAgICAKICAgIHBsb3RzW1tpXV0gPC0gcAogIH0KICAKICByZXR1cm4ocGxvdHMpCn0KCmFsbF9yX2F1dG8gPC0gc2FwcGx5KGFsbF9yZXN1bHRzW1siU01BVFNTQ1IiXV0sIGZ1bmN0aW9uKHgpIHsKICBpZihpcy5udWxsKHgpKSByZXR1cm4oMCkKICByZXR1cm4oc3FydCh4JHBlcmZvcm1hbmNlJHBvb2xlZF9yMikpCn0pCnRvcF9hdXRvX3Rpc3N1ZXMgPC0gbmFtZXMoc29ydChhbGxfcl9hdXRvLCBkZWNyZWFzaW5nID0gVFJVRSkpWzE6M10KCmF1dG9fYm94cGxvdF9wbG90cyA8LSBjcmVhdGVfYm94cGxvdF9wYW5lbChhbGxfcmVzdWx0cywgdG9wX2F1dG9fdGlzc3VlcykKYXV0b2x5c2lzX2dyaWQgPC0gZ3JpZC5hcnJhbmdlKGdyb2JzID0gYXV0b19ib3hwbG90X3Bsb3RzLCBucm93ID0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYm90dG9tID0gdGV4dEdyb2IoIkFjdHVhbCBDYXRlZ29yeSIsIGdwID0gZ3Bhcihmb250c2l6ZSA9IDE4LCBmb250ZmFjZSA9ICJib2xkIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWZ0ID0gdGV4dEdyb2IoIlByZWRpY3RlZCBTY29yZSIsIHJvdCA9IDkwLCBncCA9IGdwYXIoZm9udHNpemUgPSAxOCwgZm9udGZhY2UgPSAiYm9sZCIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wID0gdGV4dEdyb2IoIkQiLCB4ID0gMC4wMiwganVzdCA9ICJsZWZ0IiwgZ3AgPSBncGFyKGZvbnRzaXplID0gMTgsIGZvbnRmYWNlID0gImJvbGQiKSkpCgojIENvbWJpbmUgYWxsIHBhbmVscwpjb21iaW5lZF9iYXJwbG90cyA8LSBncmlkLmFycmFuZ2UocmluX2JhcnBsb3QsIGF1dG9seXNpc19iYXJwbG90LCBuY29sID0gMikKY29tYmluZWRfQ0RfcGxvdHMgPC0gZ3JpZC5hcnJhbmdlKHJpbl9ncmlkLCBhdXRvbHlzaXNfZ3JpZCwgbnJvdyA9IDIpCmZpZ3VyZTNfZmluYWwgPC0gZ3JpZC5hcnJhbmdlKGNvbWJpbmVkX2JhcnBsb3RzLCBjb21iaW5lZF9DRF9wbG90cywgbmNvbCA9IDIsIHdpZHRocyA9IGMoMC40NSwgMC41NSkpCgpnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCAiRmlndXJlM19GaW5hbF9Db21wbGV0ZS5wZGYiKSwgCiAgICAgICBmaWd1cmUzX2ZpbmFsLCB3aWR0aCA9IDIwLCBoZWlnaHQgPSAxMiwgZHBpID0gNjAwLCBiZyA9ICJ3aGl0ZSIpCmBgYAoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDYuIEZpZ3VyZSA0IC0gRmVhdHVyZSBJbXBvcnRhbmNlCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBge3J9CiMgRXh0cmFjdCB0b3AgZmVhdHVyZXMKZXh0cmFjdF9mZWF0dXJlcyA8LSBmdW5jdGlvbihyZXN1bHRzLCB0YXJnZXQsIHRvcF9uID0gMjApIHsKICBhbGxfdGlzc3VlX2ZlYXR1cmVzIDwtIGRhdGEuZnJhbWUoKQogIAogIGZvcih0aXNzdWUgaW4gbmFtZXMocmVzdWx0c1tbdGFyZ2V0XV0pKSB7CiAgICB0aXNzdWVfcmVzdWx0IDwtIHJlc3VsdHNbW3RhcmdldF1dW1t0aXNzdWVdXQogICAgaWYoaXMubnVsbCh0aXNzdWVfcmVzdWx0KSkgbmV4dAogICAgCiAgICBpZighaXMubnVsbCh0aXNzdWVfcmVzdWx0JGZlYXR1cmVfY291bnRzKSkgewogICAgICBmZWF0dXJlcyA8LSBuYW1lcyh0aXNzdWVfcmVzdWx0JGZlYXR1cmVfY291bnRzKQogICAgICBjb3VudHMgPC0gYXMubnVtZXJpYyh0aXNzdWVfcmVzdWx0JGZlYXR1cmVfY291bnRzKQogICAgICAKICAgICAgdGlzc3VlX2RmIDwtIGRhdGEuZnJhbWUoCiAgICAgICAgZmVhdHVyZSA9IGZlYXR1cmVzLAogICAgICAgIGNvdW50ID0gY291bnRzLAogICAgICAgIHRpc3N1ZSA9IHRpc3N1ZSwKICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKICAgICAgKQogICAgICBhbGxfdGlzc3VlX2ZlYXR1cmVzIDwtIHJiaW5kKGFsbF90aXNzdWVfZmVhdHVyZXMsIHRpc3N1ZV9kZikKICAgIH0KICB9CiAgCiAgZmVhdHVyZV9zdW1tYXJ5IDwtIGFsbF90aXNzdWVfZmVhdHVyZXMgJT4lCiAgICBncm91cF9ieShmZWF0dXJlKSAlPiUKICAgIHN1bW1hcml6ZSh0b3RhbF9jb3VudCA9IHN1bShjb3VudCkpICU+JQogICAgYXJyYW5nZShkZXNjKHRvdGFsX2NvdW50KSkgJT4lCiAgICBzbGljZSgxOnRvcF9uKQogIAogIGZlYXR1cmVfbWF0cml4IDwtIGFsbF90aXNzdWVfZmVhdHVyZXMgJT4lCiAgICBmaWx0ZXIoZmVhdHVyZSAlaW4lIGZlYXR1cmVfc3VtbWFyeSRmZWF0dXJlKSAlPiUKICAgIHJlc2hhcGUyOjpkY2FzdChmZWF0dXJlIH4gdGlzc3VlLCB2YWx1ZS52YXIgPSAiY291bnQiLCBmaWxsID0gMCkKICAKICByZXR1cm4obGlzdChzdW1tYXJ5ID0gZmVhdHVyZV9zdW1tYXJ5LCBtYXRyaXggPSBmZWF0dXJlX21hdHJpeCkpCn0KCnJpbl9mZWF0dXJlcyA8LSBleHRyYWN0X2ZlYXR1cmVzKGFsbF9yZXN1bHRzLCAiU01SSU4iKQphdXRvX2ZlYXR1cmVzIDwtIGV4dHJhY3RfZmVhdHVyZXMoYWxsX3Jlc3VsdHMsICJTTUFUU1NDUiIpCgojIEZpbmQgY29tbW9uIGZlYXR1cmVzCmNvbW1vbl9mZWF0dXJlcyA8LSBpbnRlcnNlY3QocmluX2ZlYXR1cmVzJHN1bW1hcnkkZmVhdHVyZSwgYXV0b19mZWF0dXJlcyRzdW1tYXJ5JGZlYXR1cmUpCnJpbl9vbmx5X2ZlYXR1cmVzIDwtIHNldGRpZmYocmluX2ZlYXR1cmVzJHN1bW1hcnkkZmVhdHVyZSwgYXV0b19mZWF0dXJlcyRzdW1tYXJ5JGZlYXR1cmUpCmF1dG9fb25seV9mZWF0dXJlcyA8LSBzZXRkaWZmKGF1dG9fZmVhdHVyZXMkc3VtbWFyeSRmZWF0dXJlLCByaW5fZmVhdHVyZXMkc3VtbWFyeSRmZWF0dXJlKQoKIyBDcmVhdGUgaGVhdG1hcHMKY3JlYXRlX2hlYXRtYXAgPC0gZnVuY3Rpb24oZmVhdHVyZV9kYXRhLCBjb2xvcl9wYWxldHRlKSB7CiAgZmVhdHVyZV9sb25nIDwtIHJlc2hhcGUyOjptZWx0KGZlYXR1cmVfZGF0YSRtYXRyaXgsIGlkLnZhcnMgPSAiZmVhdHVyZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5uYW1lID0gInRpc3N1ZSIsIHZhbHVlLm5hbWUgPSAiY291bnQiKQogIAogIGZlYXR1cmVfbG9uZyA8LSBtZXJnZShmZWF0dXJlX2xvbmcsIGZlYXR1cmVfZGF0YSRzdW1tYXJ5WywgYygiZmVhdHVyZSIsICJ0b3RhbF9jb3VudCIpXSwgYnkgPSAiZmVhdHVyZSIpCiAgZmVhdHVyZV9sb25nJGZlYXR1cmVfbGFiZWwgPC0gc3ViKCJmZWF0dXJlXyIsICIiLCBmZWF0dXJlX2xvbmckZmVhdHVyZSkKICAKICBwIDwtIGdncGxvdChmZWF0dXJlX2xvbmcsIGFlcyh4ID0gdGlzc3VlLCB5ID0gcmVvcmRlcihmZWF0dXJlX2xhYmVsLCB0b3RhbF9jb3VudCkpKSArCiAgICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBjb3VudCksIGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9IDAuMSkgKwogICAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3JzID0gY29sb3JfcGFsZXR0ZSwgbmFtZSA9ICJTZWxlY3Rpb25cbkZyZXF1ZW5jeSIpICsKICAgIGxhYnMoeCA9IE5VTEwsIHkgPSAiRmVhdHVyZSBJRCIpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICB0aGVtZSgKICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA2NSwgaGp1c3QgPSAxLCBzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpCiAgICApCiAgCiAgcmV0dXJuKHApCn0KCnJpbl9oZWF0bWFwIDwtIGNyZWF0ZV9oZWF0bWFwKHJpbl9mZWF0dXJlcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3JSYW1wUGFsZXR0ZShjKCJ3aGl0ZSIsICIjNjZDREFBIiwgIiMwMDY0MDAiKSkoMTAwKSkgKwogIGdndGl0bGUoIkEiKQoKYXV0b19oZWF0bWFwIDwtIGNyZWF0ZV9oZWF0bWFwKGF1dG9fZmVhdHVyZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yUmFtcFBhbGV0dGUoYygid2hpdGUiLCAiI0ZDOEQ2MiIsICIjRDk0ODAxIikpKDEwMCkpICsKICBnZ3RpdGxlKCJCIikKCiMgQ3JlYXRlIFZlbm4gZGlhZ3JhbQp2ZW5uX3Bsb3QgPC0gZ2dwbG90KCkgKwogIGdnZm9yY2U6Omdlb21fY2lyY2xlKGRhdGEgPSBkYXRhLmZyYW1lKHggPSBjKC0xLCAxKSwgeSA9IGMoMCwgMCksIHIgPSBjKDIsIDIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0ZWdvcnkgPSBjKCJSSU4gRmVhdHVyZXMiLCAiQXV0b2x5c2lzIEZlYXR1cmVzIikpLAogICAgICAgICAgICAgICAgICAgICAgYWVzKHgwID0geCwgeTAgPSB5LCByID0gciwgZmlsbCA9IGNhdGVnb3J5KSwKICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC41LCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAwLjgpICsKICBnZW9tX3RleHQoZGF0YSA9IGRhdGEuZnJhbWUoeCA9IGMoLTEuNjUsIDAsIDEuNjUpLCB5ID0gYygwLCAwLCAwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBjKGxlbmd0aChyaW5fb25seV9mZWF0dXJlcyksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZW5ndGgoY29tbW9uX2ZlYXR1cmVzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoKGF1dG9fb25seV9mZWF0dXJlcykpKSwKICAgICAgICAgICBhZXMoeCA9IHgsIHkgPSB5LCBsYWJlbCA9IGxhYmVsKSwgc2l6ZSA9IDEwLCBmb250ZmFjZSA9ICJib2xkIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlJJTiBGZWF0dXJlcyIgPSAiIzY2Q0RBQSIsICJBdXRvbHlzaXMgRmVhdHVyZXMiID0gIiNGQzhENjIiKSkgKwogIHRoZW1lX3ZvaWQoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgY29vcmRfZml4ZWQoKSArCiAgeGxpbSgtMywgMykgKyB5bGltKC0yLCAyKSArCiAgZ2d0aXRsZSgiQyIpCgojIENvbWJpbmUgcGxvdHMKaGVhdG1hcHMgPC0gcGxvdF9ncmlkKHJpbl9oZWF0bWFwLCBhdXRvX2hlYXRtYXAsIG5jb2wgPSAyKQpmaWd1cmU0IDwtIHBsb3RfZ3JpZChoZWF0bWFwcywgdmVubl9wbG90LCBucm93ID0gMiwgcmVsX2hlaWdodHMgPSBjKDIsIDEuNSkpCgpnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCAiRmlndXJlNF9GZWF0dXJlLnBkZiIpLCAKICAgICAgIGZpZ3VyZTQsIHdpZHRoID0gMTQsIGhlaWdodCA9IDEyLCBkcGkgPSA2MDAsIGJnID0gInRyYW5zcGFyZW50IikKYGBgCgoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDcuIEFkZGl0aW9uYWwgVmlzdWFsaXphdGlvbnMgYW5kIENvbXBhcmF0aXZlIEFuYWx5c2lzCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBge3J9CiMgQ3JlYXRlIGNvbXBhcmF0aXZlIHBlcmZvcm1hbmNlIGNoYXJ0IGZvciB0b3AgdGlzc3VlcwpjcmVhdGVfY29tcGFyYXRpdmVfYmFyX2NoYXJ0IDwtIGZ1bmN0aW9uKHJpbl9yZXN1bHRzLCBhdXRvbHlzaXNfcmVzdWx0cykgewogIGNvbW1vbl90aXNzdWVzIDwtIGludGVyc2VjdChyaW5fcmVzdWx0cyRUaXNzdWUsIGF1dG9seXNpc19yZXN1bHRzJFRpc3N1ZSkKICAKICBjb21wYXJpc29uX2RhdGEgPC0gZGF0YS5mcmFtZSgKICAgIFRpc3N1ZSA9IGNvbW1vbl90aXNzdWVzLAogICAgUklOX1IgPSByaW5fcmVzdWx0cyRSW21hdGNoKGNvbW1vbl90aXNzdWVzLCByaW5fcmVzdWx0cyRUaXNzdWUpXSwKICAgIEF1dG9seXNpc19SID0gYXV0b2x5c2lzX3Jlc3VsdHMkUlttYXRjaChjb21tb25fdGlzc3VlcywgYXV0b2x5c2lzX3Jlc3VsdHMkVGlzc3VlKV0KICApCiAgCiAgY29tcGFyaXNvbl9kYXRhJEF2ZXJhZ2VfUiA8LSAoYWJzKGNvbXBhcmlzb25fZGF0YSRSSU5fUikgKyBhYnMoY29tcGFyaXNvbl9kYXRhJEF1dG9seXNpc19SKSkgLyAyCiAgY29tcGFyaXNvbl9kYXRhIDwtIGNvbXBhcmlzb25fZGF0YVtvcmRlcigtY29tcGFyaXNvbl9kYXRhJEF2ZXJhZ2VfUiksIF0KICB0b3BfdGlzc3VlcyA8LSBoZWFkKGNvbXBhcmlzb25fZGF0YSwgMTUpCiAgCiAgcGxvdF9kYXRhIDwtIHJlc2hhcGUyOjptZWx0KHRvcF90aXNzdWVzWywgYygiVGlzc3VlIiwgIlJJTl9SIiwgIkF1dG9seXNpc19SIildLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZC52YXJzID0gIlRpc3N1ZSIsIHZhcmlhYmxlLm5hbWUgPSAiTW9kZWwiLCB2YWx1ZS5uYW1lID0gIlJfdmFsdWUiKQogIHBsb3RfZGF0YSRUaXNzdWUgPC0gZmFjdG9yKHBsb3RfZGF0YSRUaXNzdWUsIGxldmVscyA9IHRvcF90aXNzdWVzJFRpc3N1ZSkKICAKICBjb21wYXJpc29uX3Bsb3QgPC0gZ2dwbG90KHBsb3RfZGF0YSwgYWVzKHggPSBUaXNzdWUsIHkgPSBSX3ZhbHVlLCBmaWxsID0gTW9kZWwpKSArCiAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiLCBhbHBoYSA9IDAuOCkgKwogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNwcmludGYoIiUuM2YiLCBSX3ZhbHVlKSksIAogICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOSksIHZqdXN0ID0gLTAuMywgc2l6ZSA9IDMpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlJJTl9SIiA9ICIjNjZDREFBIiwgIkF1dG9seXNpc19SIiA9ICIjRkM4RDYyIiksCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIlJJTiIsICJBdXRvbHlzaXMiKSkgKwogICAgdGhlbWVfbWluaW1hbCgpICsKICAgIHRoZW1lKAogICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHNpemUgPSAxMSwgZmFjZSA9ICJib2xkIiksCiAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwKICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIKICAgICkgKwogICAgbGFicygKICAgICAgdGl0bGUgPSAiTW9kZWwgUGVyZm9ybWFuY2UgQ29tcGFyaXNvbiBBY3Jvc3MgVG9wIFRpc3N1ZXMiLAogICAgICBzdWJ0aXRsZSA9ICJUb3AgMTUgdGlzc3VlcyByYW5rZWQgYnkgYXZlcmFnZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCIsCiAgICAgIHggPSAiVGlzc3VlIFR5cGUiLAogICAgICB5ID0gIkNvcnJlbGF0aW9uIENvZWZmaWNpZW50IChSKSIsCiAgICAgIGZpbGwgPSAiTW9kZWwgVHlwZSIKICAgICkKICAKICBnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCAiY29tcGFyYXRpdmVfcGVyZm9ybWFuY2VfY2hhcnQucGRmIiksIAogICAgICAgICBjb21wYXJpc29uX3Bsb3QsIHdpZHRoID0gMTQsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkKICAKICByZXR1cm4oY29tcGFyaXNvbl9wbG90KQp9CgojIENyZWF0ZSBjb3JyZWxhdGlvbiBkaXN0cmlidXRpb24gYW5hbHlzaXMKY3JlYXRlX2NvcnJlbGF0aW9uX2Rpc3RyaWJ1dGlvbiA8LSBmdW5jdGlvbihyZXN1bHRzX2RmLCB0YXJnZXRfbmFtZSkgewogIHJfZGlzdF9wbG90IDwtIGdncGxvdChyZXN1bHRzX2RmLCBhZXMoeCA9IGFicyhSKSkpICsKICAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMC4wNSwgZmlsbCA9ICJzdGVlbGJsdWUiLCBjb2xvciA9ICJ3aGl0ZSIsIGFscGhhID0gMC44KSArCiAgICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gbWVhbihhYnMoUikpKSwgCiAgICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEuMikgKwogICAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IG1lZGlhbihhYnMoUikpKSwgCiAgICAgICAgICAgICAgY29sb3IgPSAiZGFya2dyZWVuIiwgbGluZXR5cGUgPSAiZGFzaGVkIiwgc2l6ZSA9IDEuMikgKwogICAgYW5ub3RhdGUoInRleHQiLCB4ID0gbWVhbihhYnMocmVzdWx0c19kZiRSKSkgKyAwLjA4LCB5ID0gbWF4KHRhYmxlKGN1dChhYnMocmVzdWx0c19kZiRSKSwgMjApKSkgKiAwLjgsIAogICAgICAgICAgICAgbGFiZWwgPSBwYXN0ZSgiTWVhbiA9Iiwgcm91bmQobWVhbihhYnMocmVzdWx0c19kZiRSKSksIDMpKSwKICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSBtZWRpYW4oYWJzKHJlc3VsdHNfZGYkUikpIC0gMC4wOCwgeSA9IG1heCh0YWJsZShjdXQoYWJzKHJlc3VsdHNfZGYkUiksIDIwKSkpICogMC42LCAKICAgICAgICAgICAgIGxhYmVsID0gcGFzdGUoIk1lZGlhbiA9Iiwgcm91bmQobWVkaWFuKGFicyhyZXN1bHRzX2RmJFIpKSwgMykpLAogICAgICAgICAgICAgY29sb3IgPSAiZGFya2dyZWVuIiwgZm9udGZhY2UgPSAiYm9sZCIpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBsYWJzKAogICAgICB0aXRsZSA9IHBhc3RlKCJEaXN0cmlidXRpb24gb2YgQ29ycmVsYXRpb24gQ29lZmZpY2llbnRzOiIsIHRhcmdldF9uYW1lKSwKICAgICAgc3VidGl0bGUgPSBwYXN0ZSgiQW5hbHlzaXMgb2YiLCBucm93KHJlc3VsdHNfZGYpLCAidGlzc3VlLXNwZWNpZmljIG1vZGVscyIpLAogICAgICB4ID0gIkFic29sdXRlIENvcnJlbGF0aW9uIENvZWZmaWNpZW50IHxSfCIsCiAgICAgIHkgPSAiRnJlcXVlbmN5IgogICAgKSArCiAgICB0aGVtZSgKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIpLAogICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIikKICAgICkKICAKICBnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCBwYXN0ZTAoZ3N1YigiICIsICJfIiwgdG9sb3dlcih0YXJnZXRfbmFtZSkpLCAiX2NvcnJlbGF0aW9uX2Rpc3RyaWJ1dGlvbi5wZGYiKSksIAogICAgICAgICByX2Rpc3RfcGxvdCwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNywgZHBpID0gMzAwKQogIAogIHJldHVybihyX2Rpc3RfcGxvdCkKfQoKIyBDcmVhdGUgZW5oYW5jZWQgc2NhdHRlciBwbG90cyB3aXRoIHJlc2lkdWFsIGFuYWx5c2lzCmNyZWF0ZV9lbmhhbmNlZF9zY2F0dGVyX3Bsb3RzIDwtIGZ1bmN0aW9uKHJlc3VsdHMsIHRhcmdldF9uYW1lLCB0b3BfbiA9IDYpIHsKICBhbGxfcl92YWx1ZXMgPC0gc2FwcGx5KHJlc3VsdHMsIGZ1bmN0aW9uKHgpIHsKICAgIGlmKGlzLm51bGwoeCkpIHJldHVybigwKQogICAgcmV0dXJuKGFicyh4JHBlcmZvcm1hbmNlJHBvb2xlZF9yKSkKICB9KQogIAogIHRvcF90aXNzdWVzIDwtIG5hbWVzKHNvcnQoYWxsX3JfdmFsdWVzLCBkZWNyZWFzaW5nID0gVFJVRSkpWzE6bWluKHRvcF9uLCBsZW5ndGgoYWxsX3JfdmFsdWVzKSldCiAgCiAgZm9yKHRpc3N1ZSBpbiB0b3BfdGlzc3VlcykgewogICAgaWYoaXMubnVsbChyZXN1bHRzW1t0aXNzdWVdXSkpIG5leHQKICAgIAogICAgcHJlZGljdGlvbnMgPC0gcmVzdWx0c1tbdGlzc3VlXV0kYWxsX3ByZWRpY3Rpb25zCiAgICByX3ZhbHVlIDwtIHJlc3VsdHNbW3Rpc3N1ZV1dJHBlcmZvcm1hbmNlJHBvb2xlZF9yCiAgICBybXNlX3ZhbHVlIDwtIHJlc3VsdHNbW3Rpc3N1ZV1dJHBlcmZvcm1hbmNlJHBvb2xlZF9ybXNlCiAgICAKICAgIHByZWRpY3Rpb25zJHJlc2lkdWFsIDwtIHByZWRpY3Rpb25zJHByZWRpY3RlZCAtIHByZWRpY3Rpb25zJGFjdHVhbAogICAgCiAgICBzY2F0dGVyX3Bsb3QgPC0gZ2dwbG90KHByZWRpY3Rpb25zLCBhZXMoeCA9IGFjdHVhbCwgeSA9IHByZWRpY3RlZCkpICsKICAgICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNywgY29sb3IgPSAiIzJFODZBQiIsIHNpemUgPSAyLjUpICsKICAgICAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBjb2xvciA9ICJyZWQiLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBzaXplID0gMSkgKwogICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IFRSVUUsIGNvbG9yID0gImRhcmtibHVlIiwgYWxwaGEgPSAwLjMpICsKICAgICAgbGFicygKICAgICAgICB0aXRsZSA9IHBhc3RlKHRpc3N1ZSwgIi0iLCB0YXJnZXRfbmFtZSwgIlByZWRpY3Rpb24gTW9kZWwiKSwKICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlKCJSID0iLCByb3VuZChyX3ZhbHVlLCAzKSwgInwgUk1TRSA9Iiwgcm91bmQocm1zZV92YWx1ZSwgMykpLAogICAgICAgIHggPSBwYXN0ZSgiQWN0dWFsIiwgdGFyZ2V0X25hbWUpLAogICAgICAgIHkgPSBwYXN0ZSgiUHJlZGljdGVkIiwgdGFyZ2V0X25hbWUpCiAgICAgICkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICB0aGVtZSgKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDExLCBmYWNlID0gImJvbGQiKQogICAgICApCiAgICAKICAgIGdnc2F2ZShmaWxlLnBhdGgob3V0cHV0X3BhdGgsIHBhc3RlMCgiZW5oYW5jZWRfIiwgdGlzc3VlLCAiXyIsIGdzdWIoIiAiLCAiXyIsIHRvbG93ZXIodGFyZ2V0X25hbWUpKSwgIl9zY2F0dGVyLnBkZiIpKSwgCiAgICAgICAgICAgc2NhdHRlcl9wbG90LCB3aWR0aCA9IDEwLCBoZWlnaHQgPSA4LCBkcGkgPSAzMDApCiAgfQp9CgojIEV4ZWN1dGUgY29tcGFyYXRpdmUgdmlzdWFsaXphdGlvbnMKY29tcGFyaXNvbl9jaGFydCA8LSBjcmVhdGVfY29tcGFyYXRpdmVfYmFyX2NoYXJ0KHJpbl9zdW1tYXJ5LCBhdXRvbHlzaXNfc3VtbWFyeSkKcmluX2Rpc3RyaWJ1dGlvbiA8LSBjcmVhdGVfY29ycmVsYXRpb25fZGlzdHJpYnV0aW9uKHJpbl9zdW1tYXJ5LCAiUklOIFNjb3JlIikKYXV0b2x5c2lzX2Rpc3RyaWJ1dGlvbiA8LSBjcmVhdGVfY29ycmVsYXRpb25fZGlzdHJpYnV0aW9uKGF1dG9seXNpc19zdW1tYXJ5LCAiQXV0b2x5c2lzIFNjb3JlIikKCmNyZWF0ZV9lbmhhbmNlZF9zY2F0dGVyX3Bsb3RzKGFsbF9yZXN1bHRzW1siU01SSU4iXV0sICJSSU4gU2NvcmUiLCB0b3BfbiA9IDYpCmNyZWF0ZV9lbmhhbmNlZF9zY2F0dGVyX3Bsb3RzKGFsbF9yZXN1bHRzW1siU01BVFNTQ1IiXV0sICJBdXRvbHlzaXMgU2NvcmUiLCB0b3BfbiA9IDYpCmBgYAoKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyA4LiBGaWd1cmUgNTogTW9kZWwgUGVyZm9ybWFuY2UgdnMgU2FtcGxlIFNpemUgQW5hbHlzaXMKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpgYGB7cn0KY3JlYXRlX3NhbXBsZV9zaXplX2FuYWx5c2lzIDwtIGZ1bmN0aW9uKHJpbl9zdW1tYXJ5LCBhdXRvbHlzaXNfc3VtbWFyeSkgewogIAogIGNvbWJpbmVkX2RhdGEgPC0gcmJpbmQoCiAgICBkYXRhLmZyYW1lKAogICAgICBUaXNzdWUgPSByaW5fc3VtbWFyeSRUaXNzdWUsCiAgICAgIFNhbXBsZXMgPSByaW5fc3VtbWFyeSRTYW1wbGVzLCAKICAgICAgUl92YWx1ZSA9IGFicyhyaW5fc3VtbWFyeSRSKSwKICAgICAgTW9kZWwgPSAiUklOIgogICAgKSwKICAgIGRhdGEuZnJhbWUoCiAgICAgIFRpc3N1ZSA9IGF1dG9seXNpc19zdW1tYXJ5JFRpc3N1ZSwKICAgICAgU2FtcGxlcyA9IGF1dG9seXNpc19zdW1tYXJ5JFNhbXBsZXMsCiAgICAgIFJfdmFsdWUgPSBhYnMoYXV0b2x5c2lzX3N1bW1hcnkkUiksCiAgICAgIE1vZGVsID0gIkF1dG9seXNpcyIKICAgICkKICApCiAgCiAgc2NhdHRlcl9wYW5lbCA8LSBnZ3Bsb3QoY29tYmluZWRfZGF0YSwgYWVzKHggPSBTYW1wbGVzLCB5ID0gUl92YWx1ZSwgY29sb3IgPSBNb2RlbCkpICsKICAgIGdlb21fcG9pbnQoc2l6ZSA9IDMuNSwgYWxwaGEgPSAwLjgpICsKICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVFJVRSwgYWxwaGEgPSAwLjMsIHNpemUgPSAxLjIpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJSSU4iID0gIiM2NkNEQUEiLCAiQXV0b2x5c2lzIiA9ICIjRkM4RDYyIikpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBsYWJzKAogICAgICB0aXRsZSA9ICJBIiwKICAgICAgeCA9ICJTYW1wbGUgU2l6ZSAobikiLAogICAgICB5ID0gIkNvcnJlbGF0aW9uIENvZWZmaWNpZW50IHxSfCIsCiAgICAgIHN1YnRpdGxlID0gIlJlbGF0aW9uc2hpcCBiZXR3ZWVuIHNhbXBsZSBzaXplIGFuZCBtb2RlbCBwZXJmb3JtYW5jZSIKICAgICkgKwogICAgdGhlbWUoCiAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwLCBmYWNlID0gImJvbGQiLCBoanVzdCA9IDApLAogICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpCiAgICApCiAgCiAgY29tYmluZWRfZGF0YSRzaXplX2NhdGVnb3J5IDwtIGN1dChjb21iaW5lZF9kYXRhJFNhbXBsZXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoMCwgNTAsIDEwMCwgMjAwLCA1MDAsIEluZiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygi4omkNTAiLCAiNTEtMTAwIiwgIjEwMS0yMDAiLCAiMjAxLTUwMCIsICI+NTAwIikpCiAgCiAgYmluX3N1bW1hcnkgPC0gY29tYmluZWRfZGF0YSAlPiUKICAgIGdyb3VwX2J5KHNpemVfY2F0ZWdvcnksIE1vZGVsKSAlPiUKICAgIHN1bW1hcmlzZSgKICAgICAgbWVhbl9yID0gbWVhbihSX3ZhbHVlLCBuYS5ybSA9IFRSVUUpLAogICAgICBzZV9yID0gc2QoUl92YWx1ZSwgbmEucm0gPSBUUlVFKSAvIHNxcnQobigpKSwKICAgICAgdGlzc3VlX2NvdW50ID0gbigpLAogICAgICAuZ3JvdXBzID0gJ2Ryb3AnCiAgICApCiAgCiAgYmluX3BhbmVsIDwtIGdncGxvdChiaW5fc3VtbWFyeSwgYWVzKHggPSBzaXplX2NhdGVnb3J5LCB5ID0gbWVhbl9yLCBmaWxsID0gTW9kZWwpKSArCiAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiLCBhbHBoYSA9IDAuODUsIHdpZHRoID0gMC43KSArCiAgICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gbWVhbl9yIC0gc2VfciwgeW1heCA9IG1lYW5fciArIHNlX3IpLAogICAgICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC43KSwgd2lkdGggPSAwLjI1LCBzaXplID0gMC44KSArCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gdGlzc3VlX2NvdW50KSwgCiAgICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNyksIAogICAgICAgICAgICAgIHZqdXN0ID0gLTEuNSwgc2l6ZSA9IDMuNSwgZm9udGZhY2UgPSAiYm9sZCIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlJJTiIgPSAiIzY2Q0RBQSIsICJBdXRvbHlzaXMiID0gIiNGQzhENjIiKSkgKwogICAgdGhlbWVfbWluaW1hbCgpICsKICAgIGxhYnMoCiAgICAgIHRpdGxlID0gIkIiLAogICAgICB4ID0gIlNhbXBsZSBTaXplIENhdGVnb3JpZXMiLAogICAgICB5ID0gIk1lYW4gfFJ8IMKxIFNFIiwKICAgICAgc3VidGl0bGUgPSAiUGVyZm9ybWFuY2Ugc3RyYXRpZmllZCBieSBzYW1wbGUgc2l6ZSAobnVtYmVycyBzaG93IHRpc3N1ZSBjb3VudCkiCiAgICApICsKICAgIHRoZW1lKAogICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCwgZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwKSwKICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKQogICAgKQogIAogIGZpZ3VyZTVfY29tYmluZWQgPC0gZ3JpZC5hcnJhbmdlKHNjYXR0ZXJfcGFuZWwsIGJpbl9wYW5lbCwgbmNvbCA9IDIsIHdpZHRocyA9IGMoMSwgMSkpCiAgCiAgcmV0dXJuKGZpZ3VyZTVfY29tYmluZWQpCn0KCmFuYWx5emVfc2FtcGxlX3NpemVfZWZmZWN0cyA8LSBmdW5jdGlvbihjb21iaW5lZF9kYXRhKSB7CiAgcmluX2RhdGEgPC0gY29tYmluZWRfZGF0YVtjb21iaW5lZF9kYXRhJE1vZGVsID09ICJSSU4iLCBdCiAgYXV0b19kYXRhIDwtIGNvbWJpbmVkX2RhdGFbY29tYmluZWRfZGF0YSRNb2RlbCA9PSAiQXV0b2x5c2lzIiwgXQogIAogIHJpbl9jb3IgPC0gY29yLnRlc3QocmluX2RhdGEkU2FtcGxlcywgcmluX2RhdGEkUl92YWx1ZSkKICBhdXRvX2NvciA8LSBjb3IudGVzdChhdXRvX2RhdGEkU2FtcGxlcywgYXV0b19kYXRhJFJfdmFsdWUpCiAgCiAgcmV0dXJuKGxpc3QocmluX3Rlc3QgPSByaW5fY29yLCBhdXRvbHlzaXNfdGVzdCA9IGF1dG9fY29yKSkKfQoKZmlndXJlNV9wbG90IDwtIGNyZWF0ZV9zYW1wbGVfc2l6ZV9hbmFseXNpcyhyaW5fc3VtbWFyeSwgYXV0b2x5c2lzX3N1bW1hcnkpCnNhbXBsZV9zaXplX3N0YXRzIDwtIGFuYWx5emVfc2FtcGxlX3NpemVfZWZmZWN0cyhyYmluZCgKICBkYXRhLmZyYW1lKFRpc3N1ZSA9IHJpbl9zdW1tYXJ5JFRpc3N1ZSwgU2FtcGxlcyA9IHJpbl9zdW1tYXJ5JFNhbXBsZXMsIAogICAgICAgICAgICAgUl92YWx1ZSA9IGFicyhyaW5fc3VtbWFyeSRSKSwgTW9kZWwgPSAiUklOIiksCiAgZGF0YS5mcmFtZShUaXNzdWUgPSBhdXRvbHlzaXNfc3VtbWFyeSRUaXNzdWUsIFNhbXBsZXMgPSBhdXRvbHlzaXNfc3VtbWFyeSRTYW1wbGVzLAogICAgICAgICAgICAgUl92YWx1ZSA9IGFicyhhdXRvbHlzaXNfc3VtbWFyeSRSKSwgTW9kZWwgPSAiQXV0b2x5c2lzIikKKSkKCmdnc2F2ZShmaWxlLnBhdGgob3V0cHV0X3BhdGgsICJGaWd1cmU1X1NhbXBsZV9TaXplX0FuYWx5c2lzLnBkZiIpLCAKICAgICAgIGZpZ3VyZTVfcGxvdCwgd2lkdGggPSAxNiwgaGVpZ2h0ID0gOCwgZHBpID0gNjAwLCBiZyA9ICJ3aGl0ZSIpCmBgYAoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDkuIFRvcCA1IFRpc3N1ZXMgUXVhbGl0eSBPdmVybGF5CiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBge3J9CiMgR2V0IHRvcCA1IHRpc3N1ZXMgZm9yIGVhY2ggc2NvcmUKZ2V0X3RvcDVfdGlzc3VlcyA8LSBmdW5jdGlvbih0YXJnZXRfbmFtZSkgewogIHJfdmFsdWVzIDwtIHNhcHBseShhbGxfcmVzdWx0c1tbdGFyZ2V0X25hbWVdXSwgZnVuY3Rpb24oeCkgewogICAgaWYoaXMubnVsbCh4KSB8fCBpcy5udWxsKHgkcGVyZm9ybWFuY2UkcG9vbGVkX3IyKSkgcmV0dXJuKDApCiAgIyByZXR1cm4oc3FydCh4JHBlcmZvcm1hbmNlJHBvb2xlZF9yMikpICMgdXNlIHdoZW4gcmVxdWlyZWQgZm9yIFLCsgogICAgcmV0dXJuKGFicyh4JHBlcmZvcm1hbmNlJHBvb2xlZF9yKSkgICMgVXNlIGFic29sdXRlIFIgZm9yIHJhbmtpbmcKICB9KQogIAogIHJfdmFsdWVzIDwtIHJfdmFsdWVzW3JfdmFsdWVzID4gMF0KICB0b3A1IDwtIHNvcnQocl92YWx1ZXMsIGRlY3JlYXNpbmcgPSBUUlVFKVsxOjVdCiAgCiAgcmV0dXJuKGRhdGEuZnJhbWUoCiAgICB0aXNzdWUgPSBuYW1lcyh0b3A1KSwKICAgIGNvcnJlbGF0aW9uX3IgPSBhcy5udW1lcmljKHRvcDUpCiAgKSkKfQoKdG9wNV9yaW4gPC0gZ2V0X3RvcDVfdGlzc3VlcygiU01SSU4iKQp0b3A1X2F1dG8gPC0gZ2V0X3RvcDVfdGlzc3VlcygiU01BVFNTQ1IiKQoKIyBDcmVhdGUgb3ZlcmxheSBwbG90cwpjcmVhdGVfc2ltcGxlX292ZXJsYXkgPC0gZnVuY3Rpb24odGlzc3VlX25hbWUsIHNjb3JlX3R5cGUsIGNvbG9yX3BhbGV0dGUpIHsKICB0aXNzdWVfZGF0YSA8LSBtZXJnZWRfZ3RleFt0aXNzdWVfbWFpbiA9PSB0aXNzdWVfbmFtZV0KICAKICBpZihucm93KHRpc3N1ZV9kYXRhKSA8IDEwKSB7CiAgICByZXR1cm4oZ2dwbG90KCkgKyB0aGVtZV92b2lkKCkpCiAgfQogIAogICMgR2V0IGZlYXR1cmVzIGFuZCBydW4gVU1BUAogIGZlYXR1cmVfY29scyA8LSBncmVwKCJeZmVhdHVyZV8iLCBuYW1lcyh0aXNzdWVfZGF0YSksIHZhbHVlID0gVFJVRSkKICBmZWF0dXJlX21hdHJpeCA8LSBhcy5tYXRyaXgodGlzc3VlX2RhdGFbLCAuLmZlYXR1cmVfY29sc10pCiAgZmVhdHVyZV9tYXRyaXggPC0gc2NhbGUoZmVhdHVyZV9tYXRyaXhbLCBhcHBseShmZWF0dXJlX21hdHJpeCwgMiwgdmFyKSA+IDFlLTEwXSkKICAKICBzZXQuc2VlZCgxMjMpCiAgdW1hcF9yZXN1bHQgPC0gdW1hcChmZWF0dXJlX21hdHJpeCwgbl9uZWlnaGJvcnMgPSBtaW4oMTUsIG5yb3coZmVhdHVyZV9tYXRyaXgpIC0gMSkpCiAgCiAgcGxvdF9kYXRhIDwtIGRhdGEuZnJhbWUoCiAgICBVTUFQMSA9IHVtYXBfcmVzdWx0WywgMV0sCiAgICBVTUFQMiA9IHVtYXBfcmVzdWx0WywgMl0sCiAgICBTY29yZSA9IHRpc3N1ZV9kYXRhW1tzY29yZV90eXBlXV0KICApCiAgCiAgIyBTZXBhcmF0ZSBkYXRhIHdpdGggYW5kIHdpdGhvdXQgc2NvcmVzCiAgZGF0YV93aXRoX3Njb3JlcyA8LSBwbG90X2RhdGFbIWlzLm5hKHBsb3RfZGF0YSRTY29yZSksIF0KICBkYXRhX21pc3Npbmdfc2NvcmVzIDwtIHBsb3RfZGF0YVtpcy5uYShwbG90X2RhdGEkU2NvcmUpLCBdCiAgCiAgIyBHZXQgUiB2YWx1ZQogIHRpc3N1ZV9yZXN1bHQgPC0gYWxsX3Jlc3VsdHNbW3Njb3JlX3R5cGVdXVtbdGlzc3VlX25hbWVdXQogIHJfdmFsdWUgPC0gaWYoIWlzLm51bGwodGlzc3VlX3Jlc3VsdCkpIHRpc3N1ZV9yZXN1bHQkcGVyZm9ybWFuY2UkcG9vbGVkX3IgZWxzZSAwCiMgcl92YWx1ZSA8LSBpZighaXMubnVsbCh0aXNzdWVfcmVzdWx0KSkgc3FydCh0aXNzdWVfcmVzdWx0JHBlcmZvcm1hbmNlJHBvb2xlZF9yMikgZWxzZSAwICMgdXNlIHdoZW4gcmVxdWlyZWQgZm9yIFLCsgogIAogIHAgPC0gZ2dwbG90KCkgKwogICAgZ2VvbV9wb2ludChkYXRhID0gZGF0YV9taXNzaW5nX3Njb3JlcywgYWVzKHggPSBVTUFQMSwgeSA9IFVNQVAyKSwgCiAgICAgICAgICAgICAgIGNvbG9yID0gImxpZ2h0Z3JleSIsIGFscGhhID0gMC41LCBzaXplID0gMS41KSArCiAgICBnZW9tX3BvaW50KGRhdGEgPSBkYXRhX3dpdGhfc2NvcmVzLCBhZXMoeCA9IFVNQVAxLCB5ID0gVU1BUDIsIGNvbG9yID0gU2NvcmUpLCAKICAgICAgICAgICAgICAgYWxwaGEgPSAwLjcsIHNpemUgPSAyKSArCiAgICBjb2xvcl9wYWxldHRlICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBsYWJzKAogICAgICB0aXRsZSA9IHRpc3N1ZV9uYW1lLAogICAgICBzdWJ0aXRsZSA9IHBhc3RlKCJSID0iLCBzcHJpbnRmKCIlLjNmIiwgcl92YWx1ZSkpLAogICAgICB4ID0gIlVNQVAxIiwgeSA9ICJVTUFQMiIKICAgICkKICAKICByZXR1cm4ocCkKfQoKIyBEZWZpbmUgY29sb3IgcGFsZXR0ZXMKcmluX3BhbGV0dGUgPC0gc2NhbGVfY29sb3JfdmlyaWRpc19jKG5hbWUgPSAiUklOXG5TY29yZSIsIG9wdGlvbiA9ICJ2aXJpZGlzIikKYXV0b19wYWxldHRlIDwtIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYyhuYW1lID0gIkF1dG9seXNpc1xuU2NvcmUiLCBvcHRpb24gPSAicGxhc21hIikKCiMgQ3JlYXRlIHBsb3RzCnJpbl9wbG90cyA8LSBsYXBwbHkoMTo1LCBmdW5jdGlvbihpKSB7CiAgY3JlYXRlX3NpbXBsZV9vdmVybGF5KHRvcDVfcmluJHRpc3N1ZVtpXSwgIlNNUklOIiwgcmluX3BhbGV0dGUpCn0pCgphdXRvX3Bsb3RzIDwtIGxhcHBseSgxOjUsIGZ1bmN0aW9uKGkpIHsKICBjcmVhdGVfc2ltcGxlX292ZXJsYXkodG9wNV9hdXRvJHRpc3N1ZVtpXSwgIlNNQVRTU0NSIiwgYXV0b19wYWxldHRlKQp9KQoKIyBDb21iaW5lIHBhbmVscwpwYW5lbF9hIDwtIGdyaWQuYXJyYW5nZShncm9icyA9IHJpbl9wbG90cywgbnJvdyA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgdG9wID0gdGV4dEdyb2IoIkEiLCBncCA9IGdwYXIoZm9udHNpemUgPSAyMCwgZm9udGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IDAuMDIsIGp1c3QgPSAibGVmdCIpKQoKcGFuZWxfYiA8LSBncmlkLmFycmFuZ2UoZ3JvYnMgPSBhdXRvX3Bsb3RzLCBucm93ID0gMSwKICAgICAgICAgICAgICAgICAgICAgICB0b3AgPSB0ZXh0R3JvYigiQiIsIGdwID0gZ3Bhcihmb250c2l6ZSA9IDIwLCBmb250ZmFjZSA9ICJib2xkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gMC4wMiwganVzdCA9ICJsZWZ0IikpCgpmaW5hbF9wbG90IDwtIGdyaWQuYXJyYW5nZShwYW5lbF9hLCBwYW5lbF9iLCBucm93ID0gMikKCmdnc2F2ZShmaWxlLnBhdGgob3V0cHV0X3BhdGgsICJUb3A1X1Rpc3N1ZXNfUXVhbGl0eV9PdmVybGF5X0FCLnBkZiIpLAogICAgICAgZmluYWxfcGxvdCwgd2lkdGggPSAyMCwgaGVpZ2h0ID0gMTIsIGRwaSA9IDMwMCwgYmcgPSAid2hpdGUiKQpgYGAKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAxMC4gRmlndXJlIDY6IENyb3NzLVRpc3N1ZSBGZWF0dXJlIENvbnNpc3RlbmN5IEFuYWx5c2lzCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBge3J9CmNyZWF0ZV9mZWF0dXJlX2NvbnNpc3RlbmN5X2FuYWx5c2lzIDwtIGZ1bmN0aW9uKGFsbF9yZXN1bHRzKSB7CiAgCiAgZXh0cmFjdF9jb25zaXN0ZW50X2ZlYXR1cmVzIDwtIGZ1bmN0aW9uKHRhcmdldF9yZXN1bHRzLCBtaW5fc2VsZWN0aW9ucyA9IDMpIHsKICAgIHRpc3N1ZV9mZWF0dXJlcyA8LSBsaXN0KCkKICAgIAogICAgZm9yKHRpc3N1ZSBpbiBuYW1lcyh0YXJnZXRfcmVzdWx0cykpIHsKICAgICAgaWYoIWlzLm51bGwodGFyZ2V0X3Jlc3VsdHNbW3Rpc3N1ZV1dJGZlYXR1cmVfY291bnRzKSkgewogICAgICAgIGZlYXR1cmVzIDwtIG5hbWVzKHRhcmdldF9yZXN1bHRzW1t0aXNzdWVdXSRmZWF0dXJlX2NvdW50cykKICAgICAgICBjb3VudHMgPC0gYXMubnVtZXJpYyh0YXJnZXRfcmVzdWx0c1tbdGlzc3VlXV0kZmVhdHVyZV9jb3VudHMpCiAgICAgICAgc2VsZWN0ZWRfZmVhdHVyZXMgPC0gZmVhdHVyZXNbY291bnRzID49IG1pbl9zZWxlY3Rpb25zXQogICAgICAgIHRpc3N1ZV9mZWF0dXJlc1tbdGlzc3VlXV0gPC0gc2VsZWN0ZWRfZmVhdHVyZXMKICAgICAgfQogICAgfQogICAgCiAgICByZXR1cm4odGlzc3VlX2ZlYXR1cmVzKQogIH0KICAKICByaW5fdGlzc3VlX2ZlYXR1cmVzIDwtIGV4dHJhY3RfY29uc2lzdGVudF9mZWF0dXJlcyhhbGxfcmVzdWx0c1tbIlNNUklOIl1dKQogIGF1dG9fdGlzc3VlX2ZlYXR1cmVzIDwtIGV4dHJhY3RfY29uc2lzdGVudF9mZWF0dXJlcyhhbGxfcmVzdWx0c1tbIlNNQVRTU0NSIl1dKQogIAogIGNyZWF0ZV9jb25zaXN0ZW5jeV9oZWF0bWFwIDwtIGZ1bmN0aW9uKHRpc3N1ZV9mZWF0dXJlcywgdGl0bGUsIGNvbG9yX3NjaGVtZSkgewogICAgYWxsX2ZlYXR1cmVzIDwtIHVuaXF1ZSh1bmxpc3QodGlzc3VlX2ZlYXR1cmVzKSkKICAgIGFsbF90aXNzdWVzIDwtIG5hbWVzKHRpc3N1ZV9mZWF0dXJlcykKICAgIAogICAgc2VsZWN0aW9uX21hdHJpeCA8LSBtYXRyaXgoMCwgbnJvdyA9IGxlbmd0aChhbGxfZmVhdHVyZXMpLCBuY29sID0gbGVuZ3RoKGFsbF90aXNzdWVzKSkKICAgIHJvd25hbWVzKHNlbGVjdGlvbl9tYXRyaXgpIDwtIGFsbF9mZWF0dXJlcwogICAgY29sbmFtZXMoc2VsZWN0aW9uX21hdHJpeCkgPC0gYWxsX3Rpc3N1ZXMKICAgIAogICAgZm9yKHRpc3N1ZSBpbiBhbGxfdGlzc3VlcykgewogICAgICBzZWxlY3RlZCA8LSB0aXNzdWVfZmVhdHVyZXNbW3Rpc3N1ZV1dCiAgICAgIHNlbGVjdGlvbl9tYXRyaXhbc2VsZWN0ZWQsIHRpc3N1ZV0gPC0gMQogICAgfQogICAgCiAgICBjb25zaXN0ZW5jeV9zY29yZXMgPC0gcm93U3VtcyhzZWxlY3Rpb25fbWF0cml4KSAvIG5jb2woc2VsZWN0aW9uX21hdHJpeCkKICAgIHRvcF9mZWF0dXJlcyA8LSBuYW1lcyhzb3J0KGNvbnNpc3RlbmN5X3Njb3JlcywgZGVjcmVhc2luZyA9IFRSVUUpKVsxOjI1XQogICAgcGxvdF9tYXRyaXggPC0gc2VsZWN0aW9uX21hdHJpeFt0b3BfZmVhdHVyZXMsIF0KICAgIAogICAgcGxvdF9kYXRhIDwtIGV4cGFuZC5ncmlkKEZlYXR1cmUgPSByb3duYW1lcyhwbG90X21hdHJpeCksIAogICAgICAgICAgICAgICAgICAgICAgICAgICBUaXNzdWUgPSBjb2xuYW1lcyhwbG90X21hdHJpeCkpCiAgICBwbG90X2RhdGEkU2VsZWN0ZWQgPC0gYXMudmVjdG9yKHBsb3RfbWF0cml4KQogICAgcGxvdF9kYXRhJENvbnNpc3RlbmN5X1Njb3JlIDwtIGNvbnNpc3RlbmN5X3Njb3Jlc1twbG90X2RhdGEkRmVhdHVyZV0KICAgIHBsb3RfZGF0YSRGZWF0dXJlX0lEIDwtIHN1YigiZmVhdHVyZV8iLCAiIiwgcGxvdF9kYXRhJEZlYXR1cmUpCiAgICAKICAgIGhlYXRtYXBfcGxvdCA8LSBnZ3Bsb3QocGxvdF9kYXRhLCBhZXMoeCA9IFRpc3N1ZSwgeSA9IHJlb3JkZXIoRmVhdHVyZV9JRCwgQ29uc2lzdGVuY3lfU2NvcmUpKSkgKwogICAgICBnZW9tX3RpbGUoYWVzKGZpbGwgPSBmYWN0b3IoU2VsZWN0ZWQpKSwgY29sb3IgPSAid2hpdGUiLCBzaXplID0gMC4zKSArCiAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIjAiID0gIndoaXRlIiwgIjEiID0gY29sb3Jfc2NoZW1lKSwgCiAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJTZWxlY3RlZCIsIGxhYmVscyA9IGMoIk5vIiwgIlllcyIpKSArCiAgICAgIHRoZW1lX21pbmltYWwoKSArCiAgICAgIHRoZW1lKAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDksIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA5KSwKICAgICAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpCiAgICAgICkgKwogICAgICBsYWJzKAogICAgICAgIHRpdGxlID0gdGl0bGUsCiAgICAgICAgeCA9IE5VTEwsCiAgICAgICAgeSA9ICJGZWF0dXJlIElEIgogICAgICApCiAgICAKICAgIHJldHVybihoZWF0bWFwX3Bsb3QpCiAgfQogIAogIHBhbmVsX2EgPC0gY3JlYXRlX2NvbnNpc3RlbmN5X2hlYXRtYXAocmluX3Rpc3N1ZV9mZWF0dXJlcywgIkEuIFJJTiBNb2RlbCBGZWF0dXJlIENvbnNpc3RlbmN5IiwgIiMyRThCNTciKQogIHBhbmVsX2IgPC0gY3JlYXRlX2NvbnNpc3RlbmN5X2hlYXRtYXAoYXV0b190aXNzdWVfZmVhdHVyZXMsICJCLiBBdXRvbHlzaXMgTW9kZWwgRmVhdHVyZSBDb25zaXN0ZW5jeSIsICIjRDI2OTFFIikKICAKICBjYWxjdWxhdGVfc3RhYmlsaXR5X2Rpc3RyaWJ1dGlvbiA8LSBmdW5jdGlvbih0aXNzdWVfZmVhdHVyZXMpIHsKICAgIGFsbF9mZWF0dXJlcyA8LSB1bmlxdWUodW5saXN0KHRpc3N1ZV9mZWF0dXJlcykpCiAgICBzdGFiaWxpdHlfc2NvcmVzIDwtIHNhcHBseShhbGxfZmVhdHVyZXMsIGZ1bmN0aW9uKGZlYXR1cmUpIHsKICAgICAgdGlzc3Vlc193aXRoX2ZlYXR1cmUgPC0gc3VtKHNhcHBseSh0aXNzdWVfZmVhdHVyZXMsIGZ1bmN0aW9uKHgpIGZlYXR1cmUgJWluJSB4KSkKICAgICAgcmV0dXJuKHRpc3N1ZXNfd2l0aF9mZWF0dXJlIC8gbGVuZ3RoKHRpc3N1ZV9mZWF0dXJlcykpCiAgICB9KQogICAgcmV0dXJuKHN0YWJpbGl0eV9zY29yZXMpCiAgfQogIAogIHJpbl9zdGFiaWxpdHkgPC0gY2FsY3VsYXRlX3N0YWJpbGl0eV9kaXN0cmlidXRpb24ocmluX3Rpc3N1ZV9mZWF0dXJlcykKICBhdXRvX3N0YWJpbGl0eSA8LSBjYWxjdWxhdGVfc3RhYmlsaXR5X2Rpc3RyaWJ1dGlvbihhdXRvX3Rpc3N1ZV9mZWF0dXJlcykKICAKICBzdGFiaWxpdHlfY29tcGFyaXNvbiA8LSBkYXRhLmZyYW1lKAogICAgRmVhdHVyZSA9IGMobmFtZXMocmluX3N0YWJpbGl0eSksIG5hbWVzKGF1dG9fc3RhYmlsaXR5KSksCiAgICBTdGFiaWxpdHkgPSBjKHJpbl9zdGFiaWxpdHksIGF1dG9fc3RhYmlsaXR5KSwKICAgIE1vZGVsID0gcmVwKGMoIlJJTiIsICJBdXRvbHlzaXMiKSwgYyhsZW5ndGgocmluX3N0YWJpbGl0eSksIGxlbmd0aChhdXRvX3N0YWJpbGl0eSkpKQogICkKICAKICBwYW5lbF9jIDwtIGdncGxvdChzdGFiaWxpdHlfY29tcGFyaXNvbiwgYWVzKHggPSBTdGFiaWxpdHksIGZpbGwgPSBNb2RlbCkpICsKICAgIGdlb21faGlzdG9ncmFtKGFscGhhID0gMC43NSwgYmlucyA9IDE1LCBwb3NpdGlvbiA9ICJpZGVudGl0eSIpICsKICAgIGdlb21fdmxpbmUoZGF0YSA9IHN0YWJpbGl0eV9jb21wYXJpc29uICU+JSBncm91cF9ieShNb2RlbCkgJT4lIHN1bW1hcmlzZShtZWFuX3N0YWIgPSBtZWFuKFN0YWJpbGl0eSkpLAogICAgICAgICAgICAgICBhZXMoeGludGVyY2VwdCA9IG1lYW5fc3RhYiwgY29sb3IgPSBNb2RlbCksIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxLjIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlJJTiIgPSAiIzY2Q0RBQSIsICJBdXRvbHlzaXMiID0gIiNGQzhENjIiKSkgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIlJJTiIgPSAiIzJFOEI1NyIsICJBdXRvbHlzaXMiID0gIiNEMjY5MUUiKSkgKwogICAgdGhlbWVfbWluaW1hbCgpICsKICAgIGxhYnMoCiAgICAgIHRpdGxlID0gIkMuIEZlYXR1cmUgU3RhYmlsaXR5IERpc3RyaWJ1dGlvbiIsCiAgICAgIHggPSAiU3RhYmlsaXR5IFNjb3JlIChGcmFjdGlvbiBvZiBUaXNzdWVzKSIsCiAgICAgIHkgPSAiTnVtYmVyIG9mIEZlYXR1cmVzIiwKICAgICAgc3VidGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIGNyb3NzLXRpc3N1ZSBmZWF0dXJlIGNvbnNpc3RlbmN5IgogICAgKSArCiAgICB0aGVtZSgKICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLAogICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMSwgZmFjZSA9ICJib2xkIikKICAgICkKICAKICB0b3BfcGFuZWxzIDwtIGdyaWQuYXJyYW5nZShwYW5lbF9hLCBwYW5lbF9iLCBuY29sID0gMikKICBmaWd1cmU2X2NvbWJpbmVkIDwtIGdyaWQuYXJyYW5nZSh0b3BfcGFuZWxzLCBwYW5lbF9jLCBucm93ID0gMiwgaGVpZ2h0cyA9IGMoMiwgMSkpCiAgCiAgcmV0dXJuKGZpZ3VyZTZfY29tYmluZWQpCn0KCmNhbGN1bGF0ZV9mZWF0dXJlX292ZXJsYXBfc3RhdHMgPC0gZnVuY3Rpb24oYWxsX3Jlc3VsdHMpIHsKICBleHRyYWN0X3RvcF9mZWF0dXJlcyA8LSBmdW5jdGlvbih0YXJnZXRfcmVzdWx0cywgbiA9IDUwKSB7CiAgICBmZWF0dXJlX2NvdW50cyA8LSBsaXN0KCkKICAgIGZvcih0aXNzdWUgaW4gbmFtZXModGFyZ2V0X3Jlc3VsdHMpKSB7CiAgICAgIGlmKCFpcy5udWxsKHRhcmdldF9yZXN1bHRzW1t0aXNzdWVdXSRmZWF0dXJlX2NvdW50cykpIHsKICAgICAgICBjb3VudHMgPC0gdGFyZ2V0X3Jlc3VsdHNbW3Rpc3N1ZV1dJGZlYXR1cmVfY291bnRzCiAgICAgICAgZm9yKGZlYXQgaW4gbmFtZXMoY291bnRzKSkgewogICAgICAgICAgaWYoaXMubnVsbChmZWF0dXJlX2NvdW50c1tbZmVhdF1dKSkgZmVhdHVyZV9jb3VudHNbW2ZlYXRdXSA8LSAwCiAgICAgICAgICBmZWF0dXJlX2NvdW50c1tbZmVhdF1dIDwtIGZlYXR1cmVfY291bnRzW1tmZWF0XV0gKyBjb3VudHNbW2ZlYXRdXQogICAgICAgIH0KICAgICAgfQogICAgfQogICAgCiAgICBzb3J0ZWRfZmVhdHVyZXMgPC0gc29ydCh1bmxpc3QoZmVhdHVyZV9jb3VudHMpLCBkZWNyZWFzaW5nID0gVFJVRSkKICAgIHJldHVybihuYW1lcyhzb3J0ZWRfZmVhdHVyZXMpWzE6bWluKG4sIGxlbmd0aChzb3J0ZWRfZmVhdHVyZXMpKV0pCiAgfQogIAogIHJpbl90b3AgPC0gZXh0cmFjdF90b3BfZmVhdHVyZXMoYWxsX3Jlc3VsdHNbWyJTTVJJTiJdXSkKICBhdXRvX3RvcCA8LSBleHRyYWN0X3RvcF9mZWF0dXJlcyhhbGxfcmVzdWx0c1tbIlNNQVRTU0NSIl1dKQogIAogIG92ZXJsYXBfY291bnQgPC0gbGVuZ3RoKGludGVyc2VjdChyaW5fdG9wLCBhdXRvX3RvcCkpCiAgcmluX3VuaXF1ZSA8LSBsZW5ndGgoc2V0ZGlmZihyaW5fdG9wLCBhdXRvX3RvcCkpCiAgYXV0b191bmlxdWUgPC0gbGVuZ3RoKHNldGRpZmYoYXV0b190b3AsIHJpbl90b3ApKQogIAogIHJldHVybihsaXN0KGNvbW1vbiA9IG92ZXJsYXBfY291bnQsIHJpbl91bmlxdWUgPSByaW5fdW5pcXVlLCBhdXRvX3VuaXF1ZSA9IGF1dG9fdW5pcXVlKSkKfQoKZmlndXJlNl9wbG90IDwtIGNyZWF0ZV9mZWF0dXJlX2NvbnNpc3RlbmN5X2FuYWx5c2lzKGFsbF9yZXN1bHRzKQpvdmVybGFwX3N0YXRzIDwtIGNhbGN1bGF0ZV9mZWF0dXJlX292ZXJsYXBfc3RhdHMoYWxsX3Jlc3VsdHMpCgpnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCAiRmlndXJlNl9GZWF0dXJlX0NvbnNpc3RlbmN5LnBkZiIpLCAKICAgICAgIGZpZ3VyZTZfcGxvdCwgd2lkdGggPSAxOCwgaGVpZ2h0ID0gMTQsIGRwaSA9IDYwMCwgYmcgPSAid2hpdGUiKQpgYGAKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAxMS4gRmlndXJlIDc6IE1vZGVsIFJlc2lkdWFsIEFuYWx5c2lzCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBge3J9CmNyZWF0ZV9jb21wcmVoZW5zaXZlX3Jlc2lkdWFsX2FuYWx5c2lzIDwtIGZ1bmN0aW9uKGFsbF9yZXN1bHRzLCB0b3BfbiA9IDYpIHsKICAKICBpZGVudGlmeV90b3BfdGlzc3VlcyA8LSBmdW5jdGlvbih0YXJnZXRfcmVzdWx0cywgbikgewogICAgcGVyZm9ybWFuY2Vfc2NvcmVzIDwtIHNhcHBseSh0YXJnZXRfcmVzdWx0cywgZnVuY3Rpb24oeCkgewogICAgICBpZihpcy5udWxsKHgpKSByZXR1cm4oMCkKICAgICAgcmV0dXJuKGFicyh4JHBlcmZvcm1hbmNlJHBvb2xlZF9yKSkKICAgIH0pCiAgICB0b3BfdGlzc3VlcyA8LSBuYW1lcyhzb3J0KHBlcmZvcm1hbmNlX3Njb3JlcywgZGVjcmVhc2luZyA9IFRSVUUpKVsxOm5dCiAgICByZXR1cm4odG9wX3Rpc3N1ZXNbIWlzLm5hKHRvcF90aXNzdWVzKV0pCiAgfQogIAogIHRvcF9yaW5fdGlzc3VlcyA8LSBpZGVudGlmeV90b3BfdGlzc3VlcyhhbGxfcmVzdWx0c1tbIlNNUklOIl1dLCB0b3BfbikKICB0b3BfYXV0b190aXNzdWVzIDwtIGlkZW50aWZ5X3RvcF90aXNzdWVzKGFsbF9yZXN1bHRzW1siU01BVFNTQ1IiXV0sIHRvcF9uKQogIAogIGNyZWF0ZV9yZXNpZHVhbF9wYW5lbCA8LSBmdW5jdGlvbih0aXNzdWVzLCB0YXJnZXQsIGNvbG9yLCBwYW5lbF90aXRsZSkgewogICAgcmVzaWR1YWxfcGxvdHMgPC0gbGlzdCgpCiAgICAKICAgIGZvcihpIGluIDE6bWluKDMsIGxlbmd0aCh0aXNzdWVzKSkpIHsKICAgICAgdGlzc3VlX25hbWUgPC0gdGlzc3Vlc1tpXQogICAgICByZXN1bHQgPC0gYWxsX3Jlc3VsdHNbW3RhcmdldF1dW1t0aXNzdWVfbmFtZV1dCiAgICAgIAogICAgICBpZighaXMubnVsbChyZXN1bHQpICYmICFpcy5udWxsKHJlc3VsdCRhbGxfcHJlZGljdGlvbnMpKSB7CiAgICAgICAgcHJlZGljdGlvbnMgPC0gcmVzdWx0JGFsbF9wcmVkaWN0aW9ucwogICAgICAgIHByZWRpY3Rpb25zJHJlc2lkdWFsIDwtIHByZWRpY3Rpb25zJHByZWRpY3RlZCAtIHByZWRpY3Rpb25zJGFjdHVhbAogICAgICAgIHJfdmFsdWUgPC0gcmVzdWx0JHBlcmZvcm1hbmNlJHBvb2xlZF9yCiAgICAgICAgCiAgICAgICAgcmVzaWR1YWxfcGxvdCA8LSBnZ3Bsb3QocHJlZGljdGlvbnMsIGFlcyh4ID0gYWN0dWFsLCB5ID0gcmVzaWR1YWwpKSArCiAgICAgICAgICBnZW9tX3BvaW50KGFscGhhID0gMC43LCBjb2xvciA9IGNvbG9yLCBzaXplID0gMikgKwogICAgICAgICAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDEpICsKICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsb2VzcyIsIHNlID0gVFJVRSwgY29sb3IgPSAiZGFya2JsdWUiLCBhbHBoYSA9IDAuMykgKwogICAgICAgICAgdGhlbWVfbWluaW1hbCgpICsKICAgICAgICAgIGxhYnMoCiAgICAgICAgICAgIHRpdGxlID0gcGFzdGUodGlzc3VlX25hbWUsICIoUiA9Iiwgcm91bmQocl92YWx1ZSwgMyksICIpIiksCiAgICAgICAgICAgIHggPSBpZihpID09IDMpIHBhc3RlKCJBY3R1YWwiLCBpZmVsc2UodGFyZ2V0ID09ICJTTVJJTiIsICJSSU4iLCAiQXV0b2x5c2lzIikpIGVsc2UgIiIsCiAgICAgICAgICAgIHkgPSBpZihpID09IDEpICJSZXNpZHVhbCIgZWxzZSAiIgogICAgICAgICAgKSArCiAgICAgICAgICB0aGVtZSgKICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgZmFjZSA9ICJib2xkIikKICAgICAgICAgICkKICAgICAgICAKICAgICAgICByZXNpZHVhbF9wbG90c1tbaV1dIDwtIHJlc2lkdWFsX3Bsb3QKICAgICAgfQogICAgfQogICAgCiAgICBwYW5lbCA8LSBncmlkLmFycmFuZ2UoZ3JvYnMgPSByZXNpZHVhbF9wbG90cywgbnJvdyA9IDEsCiAgICAgICAgICAgICAgICAgICAgICAgICB0b3AgPSB0ZXh0R3JvYihwYW5lbF90aXRsZSwgZ3AgPSBncGFyKGZvbnRzaXplID0gMTYsIGZvbnRmYWNlID0gImJvbGQiKSkpCiAgICByZXR1cm4ocGFuZWwpCiAgfQogIAogIHBhbmVsX2EgPC0gY3JlYXRlX3Jlc2lkdWFsX3BhbmVsKHRvcF9yaW5fdGlzc3VlcywgIlNNUklOIiwgIiM2NkNEQUEiLCAiQS4gUklOIE1vZGVsIFJlc2lkdWFscyIpCiAgcGFuZWxfYiA8LSBjcmVhdGVfcmVzaWR1YWxfcGFuZWwodG9wX2F1dG9fdGlzc3VlcywgIlNNQVRTU0NSIiwgIiNGQzhENjIiLCAiQi4gQXV0b2x5c2lzIE1vZGVsIFJlc2lkdWFscyIpCiAgCiAgY3JlYXRlX3Jlc2lkdWFsX2Rpc3RyaWJ1dGlvbiA8LSBmdW5jdGlvbigpIHsKICAgIGFsbF9yZXNpZHVhbHMgPC0gZGF0YS5mcmFtZSgpCiAgICAKICAgIGZvcih0YXJnZXQgaW4gYygiU01SSU4iLCAiU01BVFNTQ1IiKSkgewogICAgICBmb3IodGlzc3VlIGluIG5hbWVzKGFsbF9yZXN1bHRzW1t0YXJnZXRdXSkpIHsKICAgICAgICByZXN1bHQgPC0gYWxsX3Jlc3VsdHNbW3RhcmdldF1dW1t0aXNzdWVdXQogICAgICAgIGlmKCFpcy5udWxsKHJlc3VsdCkgJiYgIWlzLm51bGwocmVzdWx0JGFsbF9wcmVkaWN0aW9ucykpIHsKICAgICAgICAgIHByZWRpY3Rpb25zIDwtIHJlc3VsdCRhbGxfcHJlZGljdGlvbnMKICAgICAgICAgIHJlc2lkdWFsX2RhdGEgPC0gZGF0YS5mcmFtZSgKICAgICAgICAgICAgdGlzc3VlID0gdGlzc3VlLAogICAgICAgICAgICB0YXJnZXQgPSBpZmVsc2UodGFyZ2V0ID09ICJTTVJJTiIsICJSSU4iLCAiQXV0b2x5c2lzIiksCiAgICAgICAgICAgIHJlc2lkdWFsID0gcHJlZGljdGlvbnMkcHJlZGljdGVkIC0gcHJlZGljdGlvbnMkYWN0dWFsLAogICAgICAgICAgICBhYnNfcmVzaWR1YWwgPSBhYnMocHJlZGljdGlvbnMkcHJlZGljdGVkIC0gcHJlZGljdGlvbnMkYWN0dWFsKQogICAgICAgICAgKQogICAgICAgICAgYWxsX3Jlc2lkdWFscyA8LSByYmluZChhbGxfcmVzaWR1YWxzLCByZXNpZHVhbF9kYXRhKQogICAgICAgIH0KICAgICAgfQogICAgfQogICAgCiAgICBkaXN0cmlidXRpb25fcGxvdCA8LSBnZ3Bsb3QoYWxsX3Jlc2lkdWFscywgYWVzKHggPSBhYnNfcmVzaWR1YWwsIGZpbGwgPSB0YXJnZXQpKSArCiAgICAgIGdlb21faGlzdG9ncmFtKGFscGhhID0gMC43NSwgYmlucyA9IDMwLCBwb3NpdGlvbiA9ICJpZGVudGl0eSIpICsKICAgICAgZ2VvbV92bGluZShkYXRhID0gYWxsX3Jlc2lkdWFscyAlPiUgZ3JvdXBfYnkodGFyZ2V0KSAlPiUgc3VtbWFyaXNlKG1lYW5fcmVzID0gbWVhbihhYnNfcmVzaWR1YWwpKSwKICAgICAgICAgICAgICAgICBhZXMoeGludGVyY2VwdCA9IG1lYW5fcmVzLCBjb2xvciA9IHRhcmdldCksIGxpbmV0eXBlID0gImRhc2hlZCIsIHNpemUgPSAxLjIpICsKICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiUklOIiA9ICIjNjZDREFBIiwgIkF1dG9seXNpcyIgPSAiI0ZDOEQ2MiIpKSArCiAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJSSU4iID0gIiMyRThCNTciLCAiQXV0b2x5c2lzIiA9ICIjRDI2OTFFIikpICsKICAgICAgdGhlbWVfbWluaW1hbCgpICsKICAgICAgbGFicygKICAgICAgICB0aXRsZSA9ICJDLiBEaXN0cmlidXRpb24gb2YgQWJzb2x1dGUgUmVzaWR1YWxzIiwKICAgICAgICB4ID0gIkFic29sdXRlIFJlc2lkdWFsIE1hZ25pdHVkZSIsCiAgICAgICAgeSA9ICJGcmVxdWVuY3kiLAogICAgICAgIGZpbGwgPSAiTW9kZWwgVHlwZSIsCiAgICAgICAgc3VidGl0bGUgPSAiQ29tcGFyaXNvbiBvZiBwcmVkaWN0aW9uIGVycm9ycyBhY3Jvc3MgYWxsIHRpc3N1ZSBtb2RlbHMiCiAgICAgICkgKwogICAgICB0aGVtZSgKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDExLCBmYWNlID0gImJvbGQiKQogICAgICApCiAgICAKICAgIHJldHVybihkaXN0cmlidXRpb25fcGxvdCkKICB9CiAgCiAgY3JlYXRlX3BlcmZvcm1hbmNlX3Jlc2lkdWFsX3JlbGF0aW9uc2hpcCA8LSBmdW5jdGlvbigpIHsKICAgIHBlcmZvcm1hbmNlX3Jlc2lkdWFsX2RhdGEgPC0gZGF0YS5mcmFtZSgpCiAgICAKICAgIGZvcih0YXJnZXQgaW4gYygiU01SSU4iLCAiU01BVFNTQ1IiKSkgewogICAgICBmb3IodGlzc3VlIGluIG5hbWVzKGFsbF9yZXN1bHRzW1t0YXJnZXRdXSkpIHsKICAgICAgICByZXN1bHQgPC0gYWxsX3Jlc3VsdHNbW3RhcmdldF1dW1t0aXNzdWVdXQogICAgICAgIGlmKCFpcy5udWxsKHJlc3VsdCkgJiYgIWlzLm51bGwocmVzdWx0JGFsbF9wcmVkaWN0aW9ucykpIHsKICAgICAgICAgIHByZWRpY3Rpb25zIDwtIHJlc3VsdCRhbGxfcHJlZGljdGlvbnMKICAgICAgICAgIAogICAgICAgICAgcGVyZl9kYXRhIDwtIGRhdGEuZnJhbWUoCiAgICAgICAgICAgIHRpc3N1ZSA9IHRpc3N1ZSwKICAgICAgICAgICAgdGFyZ2V0ID0gaWZlbHNlKHRhcmdldCA9PSAiU01SSU4iLCAiUklOIiwgIkF1dG9seXNpcyIpLAogICAgICAgICAgICByX3ZhbHVlID0gYWJzKHJlc3VsdCRwZXJmb3JtYW5jZSRwb29sZWRfciksCiAgICAgICAgICAgIHJtc2UgPSByZXN1bHQkcGVyZm9ybWFuY2UkcG9vbGVkX3Jtc2UsCiAgICAgICAgICAgIG1lYW5fYWJzX3Jlc2lkdWFsID0gbWVhbihhYnMocHJlZGljdGlvbnMkcHJlZGljdGVkIC0gcHJlZGljdGlvbnMkYWN0dWFsKSkKICAgICAgICAgICkKICAgICAgICAgIHBlcmZvcm1hbmNlX3Jlc2lkdWFsX2RhdGEgPC0gcmJpbmQocGVyZm9ybWFuY2VfcmVzaWR1YWxfZGF0YSwgcGVyZl9kYXRhKQogICAgICAgIH0KICAgICAgfQogICAgfQogICAgCiAgICByZWxhdGlvbnNoaXBfcGxvdCA8LSBnZ3Bsb3QocGVyZm9ybWFuY2VfcmVzaWR1YWxfZGF0YSwgYWVzKHggPSByX3ZhbHVlLCB5ID0gbWVhbl9hYnNfcmVzaWR1YWwsIGNvbG9yID0gdGFyZ2V0KSkgKwogICAgICBnZW9tX3BvaW50KHNpemUgPSAzLjUsIGFscGhhID0gMC44KSArCiAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVFJVRSwgYWxwaGEgPSAwLjMsIHNpemUgPSAxLjIpICsKICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIlJJTiIgPSAiIzY2Q0RBQSIsICJBdXRvbHlzaXMiID0gIiNGQzhENjIiKSkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKAogICAgICAgIHRpdGxlID0gIkQuIE1vZGVsIFBlcmZvcm1hbmNlIHZzIFJlc2lkdWFsIE1hZ25pdHVkZSIsCiAgICAgICAgeCA9ICJDb3JyZWxhdGlvbiBDb2VmZmljaWVudCB8UnwiLAogICAgICAgIHkgPSAiTWVhbiBBYnNvbHV0ZSBSZXNpZHVhbCIsCiAgICAgICAgY29sb3IgPSAiTW9kZWwgVHlwZSIsCiAgICAgICAgc3VidGl0bGUgPSAiUmVsYXRpb25zaGlwIGJldHdlZW4gcHJlZGljdGlvbiBhY2N1cmFjeSBhbmQgZXJyb3IgbWFnbml0dWRlIgogICAgICApICsKICAgICAgdGhlbWUoCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEsIGZhY2UgPSAiYm9sZCIpCiAgICAgICkKICAgIAogICAgcmV0dXJuKHJlbGF0aW9uc2hpcF9wbG90KQogIH0KICAKICBwYW5lbF9jIDwtIGNyZWF0ZV9yZXNpZHVhbF9kaXN0cmlidXRpb24oKQogIHBhbmVsX2QgPC0gY3JlYXRlX3BlcmZvcm1hbmNlX3Jlc2lkdWFsX3JlbGF0aW9uc2hpcCgpCiAgCiAgYm90dG9tX3BhbmVscyA8LSBncmlkLmFycmFuZ2UocGFuZWxfYywgcGFuZWxfZCwgbmNvbCA9IDIpCiAgZmlndXJlN19jb21iaW5lZCA8LSBncmlkLmFycmFuZ2UocGFuZWxfYSwgcGFuZWxfYiwgYm90dG9tX3BhbmVscywgbnJvdyA9IDMsIGhlaWdodHMgPSBjKDEsIDEsIDEpKQogIAogIHJldHVybihmaWd1cmU3X2NvbWJpbmVkKQp9CgphbmFseXplX3Jlc2lkdWFsX3BhdHRlcm5zIDwtIGZ1bmN0aW9uKGFsbF9yZXN1bHRzKSB7CiAgcmVzaWR1YWxfc3RhdHMgPC0gZGF0YS5mcmFtZSgpCiAgCiAgZm9yKHRhcmdldCBpbiBjKCJTTVJJTiIsICJTTUFUU1NDUiIpKSB7CiAgICBmb3IodGlzc3VlIGluIG5hbWVzKGFsbF9yZXN1bHRzW1t0YXJnZXRdXSkpIHsKICAgICAgcmVzdWx0IDwtIGFsbF9yZXN1bHRzW1t0YXJnZXRdXVtbdGlzc3VlXV0KICAgICAgaWYoIWlzLm51bGwocmVzdWx0KSAmJiAhaXMubnVsbChyZXN1bHQkYWxsX3ByZWRpY3Rpb25zKSkgewogICAgICAgIHByZWRpY3Rpb25zIDwtIHJlc3VsdCRhbGxfcHJlZGljdGlvbnMKICAgICAgICByZXNpZHVhbHMgPC0gcHJlZGljdGlvbnMkcHJlZGljdGVkIC0gcHJlZGljdGlvbnMkYWN0dWFsCiAgICAgICAgCiAgICAgICAgc3RhdHNfcm93IDwtIGRhdGEuZnJhbWUoCiAgICAgICAgICB0aXNzdWUgPSB0aXNzdWUsCiAgICAgICAgICB0YXJnZXQgPSB0YXJnZXQsCiAgICAgICAgICBtZWFuX3Jlc2lkdWFsID0gbWVhbihyZXNpZHVhbHMpLAogICAgICAgICAgc2RfcmVzaWR1YWwgPSBzZChyZXNpZHVhbHMpLAogICAgICAgICAgc2tld25lc3MgPSBlMTA3MTo6c2tld25lc3MocmVzaWR1YWxzKSwKICAgICAgICAgIG5vcm1hbGl0eV9wID0gc2hhcGlyby50ZXN0KHNhbXBsZShyZXNpZHVhbHMsIG1pbig1MDAwLCBsZW5ndGgocmVzaWR1YWxzKSkpKSRwLnZhbHVlCiAgICAgICAgKQogICAgICAgIHJlc2lkdWFsX3N0YXRzIDwtIHJiaW5kKHJlc2lkdWFsX3N0YXRzLCBzdGF0c19yb3cpCiAgICAgIH0KICAgIH0KICB9CiAgCiAgcmV0dXJuKHJlc2lkdWFsX3N0YXRzKQp9CgpmaWd1cmU3X3Bsb3QgPC0gY3JlYXRlX2NvbXByZWhlbnNpdmVfcmVzaWR1YWxfYW5hbHlzaXMoYWxsX3Jlc3VsdHMpCnJlc2lkdWFsX3N0YXRpc3RpY3MgPC0gYW5hbHl6ZV9yZXNpZHVhbF9wYXR0ZXJucyhhbGxfcmVzdWx0cykKCmdnc2F2ZShmaWxlLnBhdGgob3V0cHV0X3BhdGgsICJGaWd1cmU3X1Jlc2lkdWFsX0FuYWx5c2lzLnBkZiIpLCAKICAgICAgIGZpZ3VyZTdfcGxvdCwgd2lkdGggPSAxNiwgaGVpZ2h0ID0gMTQsIGRwaSA9IDYwMCwgYmcgPSAid2hpdGUiKQpgYGAKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAxMS4gRmlndXJlIDg6IEJpb2xvZ2ljYWwgRmVhdHVyZSBJbnRlcnByZXRhdGlvbgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmBgYHtyfQpjcmVhdGVfYmlvbG9naWNhbF9pbnRlcnByZXRhdGlvbl9hbmFseXNpcyA8LSBmdW5jdGlvbihhbGxfcmVzdWx0cywgbWVyZ2VkX2d0ZXgpIHsKICAKICBhbmFseXplX2ZlYXR1cmVfY2F0ZWdvcmllcyA8LSBmdW5jdGlvbigpIHsKICAgIGV4dHJhY3RfaW1wb3J0YW50X2ZlYXR1cmVzIDwtIGZ1bmN0aW9uKHRhcmdldF9yZXN1bHRzLCBuID0gNTApIHsKICAgICAgZmVhdHVyZV9pbXBvcnRhbmNlIDwtIGMoKQogICAgICBmb3IodGlzc3VlIGluIG5hbWVzKHRhcmdldF9yZXN1bHRzKSkgewogICAgICAgIGlmKCFpcy5udWxsKHRhcmdldF9yZXN1bHRzW1t0aXNzdWVdXSRmZWF0dXJlX2NvdW50cykpIHsKICAgICAgICAgIGZlYXR1cmVzIDwtIG5hbWVzKHRhcmdldF9yZXN1bHRzW1t0aXNzdWVdXSRmZWF0dXJlX2NvdW50cykKICAgICAgICAgIGNvdW50cyA8LSBhcy5udW1lcmljKHRhcmdldF9yZXN1bHRzW1t0aXNzdWVdXSRmZWF0dXJlX2NvdW50cykKICAgICAgICAgIGZlYXR1cmVfaW1wb3J0YW5jZSA8LSBjKGZlYXR1cmVfaW1wb3J0YW5jZSwgcmVwKGZlYXR1cmVzLCBjb3VudHMpKQogICAgICAgIH0KICAgICAgfQogICAgICAKICAgICAgaW1wb3J0YW5jZV90YWJsZSA8LSBzb3J0KHRhYmxlKGZlYXR1cmVfaW1wb3J0YW5jZSksIGRlY3JlYXNpbmcgPSBUUlVFKQogICAgICByZXR1cm4obmFtZXMoaW1wb3J0YW5jZV90YWJsZSlbMTptaW4obiwgbGVuZ3RoKGltcG9ydGFuY2VfdGFibGUpKV0pCiAgICB9CiAgICAKICAgIHJpbl9pbXBvcnRhbnQgPC0gZXh0cmFjdF9pbXBvcnRhbnRfZmVhdHVyZXMoYWxsX3Jlc3VsdHNbWyJTTVJJTiJdXSkKICAgIGF1dG9faW1wb3J0YW50IDwtIGV4dHJhY3RfaW1wb3J0YW50X2ZlYXR1cmVzKGFsbF9yZXN1bHRzW1siU01BVFNTQ1IiXV0pCiAgICAKICAgIGNhdGVnb3JpemVfZmVhdHVyZXMgPC0gZnVuY3Rpb24oZmVhdHVyZV9uYW1lcykgewogICAgICBmZWF0dXJlX251bWJlcnMgPC0gYXMubnVtZXJpYyhzdWIoImZlYXR1cmVfIiwgIiIsIGZlYXR1cmVfbmFtZXMpKQogICAgICBjYXRlZ29yaWVzIDwtIGNhc2Vfd2hlbigKICAgICAgICBmZWF0dXJlX251bWJlcnMgPD0gMTAyNCB+ICJNZWFuIiwKICAgICAgICBmZWF0dXJlX251bWJlcnMgPD0gMjA0OCB+ICJTdGFuZGFyZCBEZXZpYXRpb24iLAogICAgICAgIGZlYXR1cmVfbnVtYmVycyA8PSAzMDcyIH4gIk1pbmltdW0iLAogICAgICAgIFRSVUUgfiAiTWF4aW11bSIKICAgICAgKQogICAgICByZXR1cm4oY2F0ZWdvcmllcykKICAgIH0KICAgIAogICAgcmluX2NhdGVnb3JpZXMgPC0gY2F0ZWdvcml6ZV9mZWF0dXJlcyhyaW5faW1wb3J0YW50KQogICAgYXV0b19jYXRlZ29yaWVzIDwtIGNhdGVnb3JpemVfZmVhdHVyZXMoYXV0b19pbXBvcnRhbnQpCiAgICAKICAgIGNhdGVnb3J5X3N1bW1hcnkgPC0gZGF0YS5mcmFtZSgKICAgICAgQ2F0ZWdvcnkgPSByZXAoYygiTWVhbiIsICJTdGFuZGFyZCBEZXZpYXRpb24iLCAiTWluaW11bSIsICJNYXhpbXVtIiksIDIpLAogICAgICBDb3VudCA9IGModGFibGUocmluX2NhdGVnb3JpZXMpW2MoIk1lYW4iLCAiU3RhbmRhcmQgRGV2aWF0aW9uIiwgIk1pbmltdW0iLCAiTWF4aW11bSIpXSwKICAgICAgICAgICAgICAgdGFibGUoYXV0b19jYXRlZ29yaWVzKVtjKCJNZWFuIiwgIlN0YW5kYXJkIERldmlhdGlvbiIsICJNaW5pbXVtIiwgIk1heGltdW0iKV0pLAogICAgICBNb2RlbCA9IHJlcChjKCJSSU4iLCAiQXV0b2x5c2lzIiksIGVhY2ggPSA0KQogICAgKQogICAgCiAgICBjYXRlZ29yeV9zdW1tYXJ5JENvdW50W2lzLm5hKGNhdGVnb3J5X3N1bW1hcnkkQ291bnQpXSA8LSAwCiAgICAKICAgIGNhdGVnb3J5X3Bsb3QgPC0gZ2dwbG90KGNhdGVnb3J5X3N1bW1hcnksIGFlcyh4ID0gQ2F0ZWdvcnksIHkgPSBDb3VudCwgZmlsbCA9IE1vZGVsKSkgKwogICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiLCBhbHBoYSA9IDAuODUsIHdpZHRoID0gMC43KSArCiAgICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBDb3VudCksIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjcpLCAKICAgICAgICAgICAgICAgIHZqdXN0ID0gLTAuMywgc2l6ZSA9IDQsIGZvbnRmYWNlID0gImJvbGQiKSArCiAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIlJJTiIgPSAiIzY2Q0RBQSIsICJBdXRvbHlzaXMiID0gIiNGQzhENjIiKSkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKAogICAgICAgIHRpdGxlID0gIkEuIEZlYXR1cmUgQ2F0ZWdvcnkgRGlzdHJpYnV0aW9uIiwKICAgICAgICB4ID0gIlN0YXRpc3RpY2FsIFByb3BlcnR5IENhdGVnb3J5IiwKICAgICAgICB5ID0gIk51bWJlciBvZiBJbXBvcnRhbnQgRmVhdHVyZXMiLAogICAgICAgIHN1YnRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBrZXkgZmVhdHVyZXMgYWNyb3NzIHN0YXRpc3RpY2FsIG1lYXN1cmVzIgogICAgICApICsKICAgICAgdGhlbWUoCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDExLCBmYWNlID0gImJvbGQiKQogICAgICApCiAgICAKICAgIHJldHVybihjYXRlZ29yeV9wbG90KQogIH0KICAKICBjcmVhdGVfdGlzc3VlX3ZhcmlhYmlsaXR5X2FuYWx5c2lzIDwtIGZ1bmN0aW9uKCkgewogICAgZmVhdHVyZV9jb2xzIDwtIGdyZXAoIl5mZWF0dXJlXyIsIG5hbWVzKG1lcmdlZF9ndGV4KSwgdmFsdWUgPSBUUlVFKQogICAgCiAgICBnZXRfZmVhdHVyZV9pbXBvcnRhbmNlIDwtIGZ1bmN0aW9uKHRhcmdldF9yZXN1bHRzKSB7CiAgICAgIGZlYXR1cmVfc2NvcmVzIDwtIGxpc3QoKQogICAgICBmb3IodGlzc3VlIGluIG5hbWVzKHRhcmdldF9yZXN1bHRzKSkgewogICAgICAgIGlmKCFpcy5udWxsKHRhcmdldF9yZXN1bHRzW1t0aXNzdWVdXSRmZWF0dXJlX2NvdW50cykpIHsKICAgICAgICAgIGNvdW50cyA8LSB0YXJnZXRfcmVzdWx0c1tbdGlzc3VlXV0kZmVhdHVyZV9jb3VudHMKICAgICAgICAgIGZvcihmZWF0IGluIG5hbWVzKGNvdW50cykpIHsKICAgICAgICAgICAgaWYoaXMubnVsbChmZWF0dXJlX3Njb3Jlc1tbZmVhdF1dKSkgZmVhdHVyZV9zY29yZXNbW2ZlYXRdXSA8LSAwCiAgICAgICAgICAgIGZlYXR1cmVfc2NvcmVzW1tmZWF0XV0gPC0gZmVhdHVyZV9zY29yZXNbW2ZlYXRdXSArIGNvdW50c1tbZmVhdF1dCiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9CiAgICAgIAogICAgICBzb3J0ZWRfc2NvcmVzIDwtIHNvcnQodW5saXN0KGZlYXR1cmVfc2NvcmVzKSwgZGVjcmVhc2luZyA9IFRSVUUpCiAgICAgIHJldHVybihuYW1lcyhzb3J0ZWRfc2NvcmVzKVsxOm1pbigyNSwgbGVuZ3RoKHNvcnRlZF9zY29yZXMpKV0pCiAgICB9CiAgICAKICAgIGltcG9ydGFudF9mZWF0dXJlcyA8LSBnZXRfZmVhdHVyZV9pbXBvcnRhbmNlKGFsbF9yZXN1bHRzW1siU01SSU4iXV0pCiAgICB2YXJpYWJpbGl0eV9kYXRhIDwtIGRhdGEuZnJhbWUoKQogICAgCiAgICBmb3IoZmVhdHVyZSBpbiBpbXBvcnRhbnRfZmVhdHVyZXMpIHsKICAgICAgaWYoZmVhdHVyZSAlaW4lIGZlYXR1cmVfY29scykgewogICAgICAgIHRpc3N1ZV9zdGF0aXN0aWNzIDwtIG1lcmdlZF9ndGV4WywgLigKICAgICAgICAgIG1lYW5fdmFsdWUgPSBtZWFuKGdldChmZWF0dXJlKSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgIHNkX3ZhbHVlID0gc2QoZ2V0KGZlYXR1cmUpLCBuYS5ybSA9IFRSVUUpCiAgICAgICAgKSwgYnkgPSB0aXNzdWVfbWFpbl0KICAgICAgICAKICAgICAgICBvdmVyYWxsX3ZhcmlhbmNlIDwtIHZhcih0aXNzdWVfc3RhdGlzdGljcyRtZWFuX3ZhbHVlLCBuYS5ybSA9IFRSVUUpCiAgICAgICAgCiAgICAgICAgaW1wb3J0YW5jZV9zY29yZSA8LSBzdW0oc2FwcGx5KGFsbF9yZXN1bHRzW1siU01SSU4iXV0sIGZ1bmN0aW9uKHgpIHsKICAgICAgICAgIGlmKCFpcy5udWxsKHgkZmVhdHVyZV9jb3VudHMpICYmIGZlYXR1cmUgJWluJSBuYW1lcyh4JGZlYXR1cmVfY291bnRzKSkgewogICAgICAgICAgICByZXR1cm4oeCRmZWF0dXJlX2NvdW50c1tbZmVhdHVyZV1dKQogICAgICAgICAgfQogICAgICAgICAgcmV0dXJuKDApCiAgICAgICAgfSkpCiAgICAgICAgCiAgICAgICAgZmVhdHVyZV9udW0gPC0gYXMubnVtZXJpYyhzdWIoImZlYXR1cmVfIiwgIiIsIGZlYXR1cmUpKQogICAgICAgIGNhdGVnb3J5IDwtIGNhc2Vfd2hlbigKICAgICAgICAgIGZlYXR1cmVfbnVtIDw9IDEwMjQgfiAiTWVhbiIsCiAgICAgICAgICBmZWF0dXJlX251bSA8PSAyMDQ4IH4gIlN0ZCBEZXYiLAogICAgICAgICAgZmVhdHVyZV9udW0gPD0gMzA3MiB+ICJNaW5pbXVtIiwKICAgICAgICAgIFRSVUUgfiAiTWF4aW11bSIKICAgICAgICApCiAgICAgICAgCiAgICAgICAgdmFyaWFiaWxpdHlfZGF0YSA8LSByYmluZCh2YXJpYWJpbGl0eV9kYXRhLCBkYXRhLmZyYW1lKAogICAgICAgICAgZmVhdHVyZSA9IGZlYXR1cmUsCiAgICAgICAgICB0aXNzdWVfdmFyaWFuY2UgPSBvdmVyYWxsX3ZhcmlhbmNlLAogICAgICAgICAgaW1wb3J0YW5jZV9zY29yZSA9IGltcG9ydGFuY2Vfc2NvcmUsCiAgICAgICAgICBjYXRlZ29yeSA9IGNhdGVnb3J5CiAgICAgICAgKSkKICAgICAgfQogICAgfQogICAgCiAgICB2YXJpYWJpbGl0eV9wbG90IDwtIGdncGxvdCh2YXJpYWJpbGl0eV9kYXRhLCBhZXMoeCA9IHRpc3N1ZV92YXJpYW5jZSwgeSA9IGltcG9ydGFuY2Vfc2NvcmUsIGNvbG9yID0gY2F0ZWdvcnkpKSArCiAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDQsIGFscGhhID0gMC44KSArCiAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVFJVRSwgYWxwaGEgPSAwLjMsIGNvbG9yID0gImRhcmtibHVlIikgKwogICAgICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIiwgbmFtZSA9ICJGZWF0dXJlXG5DYXRlZ29yeSIpICsKICAgICAgdGhlbWVfbWluaW1hbCgpICsKICAgICAgbGFicygKICAgICAgICB0aXRsZSA9ICJCLiBGZWF0dXJlIEltcG9ydGFuY2UgdnMgVGlzc3VlIFZhcmlhYmlsaXR5IiwKICAgICAgICB4ID0gIlZhcmlhbmNlIEFjcm9zcyBUaXNzdWUgVHlwZXMiLAogICAgICAgIHkgPSAiSW1wb3J0YW5jZSBTY29yZSAoU2VsZWN0aW9uIEZyZXF1ZW5jeSkiLAogICAgICAgIHN1YnRpdGxlID0gIlJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRpc3N1ZSBkaXNjcmltaW5hdGlvbiBhbmQgZmVhdHVyZSBpbXBvcnRhbmNlIgogICAgICApICsKICAgICAgdGhlbWUoCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKQogICAgICApCiAgICAKICAgIHJldHVybih2YXJpYWJpbGl0eV9wbG90KQogIH0KICAKICBjcmVhdGVfZmVhdHVyZV9jb3JyZWxhdGlvbl9tYXRyaXggPC0gZnVuY3Rpb24oKSB7CiAgICBleHRyYWN0X2NvbnNlbnN1c19mZWF0dXJlcyA8LSBmdW5jdGlvbih0YXJnZXRfcmVzdWx0cywgbiA9IDIwKSB7CiAgICAgIGZlYXR1cmVfY291bnRzIDwtIGxpc3QoKQogICAgICBmb3IodGlzc3VlIGluIG5hbWVzKHRhcmdldF9yZXN1bHRzKSkgewogICAgICAgIGlmKCFpcy5udWxsKHRhcmdldF9yZXN1bHRzW1t0aXNzdWVdXSRmZWF0dXJlX2NvdW50cykpIHsKICAgICAgICAgIGNvdW50cyA8LSB0YXJnZXRfcmVzdWx0c1tbdGlzc3VlXV0kZmVhdHVyZV9jb3VudHMKICAgICAgICAgIGZvcihmZWF0IGluIG5hbWVzKGNvdW50cykpIHsKICAgICAgICAgICAgaWYoaXMubnVsbChmZWF0dXJlX2NvdW50c1tbZmVhdF1dKSkgZmVhdHVyZV9jb3VudHNbW2ZlYXRdXSA8LSAwCiAgICAgICAgICAgIGZlYXR1cmVfY291bnRzW1tmZWF0XV0gPC0gZmVhdHVyZV9jb3VudHNbW2ZlYXRdXSArIGNvdW50c1tbZmVhdF1dCiAgICAgICAgICB9CiAgICAgICAgfQogICAgICB9CiAgICAgIAogICAgICBzb3J0ZWRfZmVhdHVyZXMgPC0gc29ydCh1bmxpc3QoZmVhdHVyZV9jb3VudHMpLCBkZWNyZWFzaW5nID0gVFJVRSkKICAgICAgcmV0dXJuKG5hbWVzKHNvcnRlZF9mZWF0dXJlcylbMTptaW4obiwgbGVuZ3RoKHNvcnRlZF9mZWF0dXJlcykpXSkKICAgIH0KICAgIAogICAgY29uc2Vuc3VzX2ZlYXR1cmVzIDwtIGV4dHJhY3RfY29uc2Vuc3VzX2ZlYXR1cmVzKGFsbF9yZXN1bHRzW1siU01SSU4iXV0sIDE1KQogICAgCiAgICBpZihsZW5ndGgoY29uc2Vuc3VzX2ZlYXR1cmVzKSA+IDEpIHsKICAgICAgZmVhdHVyZV9tYXRyaXggPC0gYXMubWF0cml4KG1lcmdlZF9ndGV4WywgLi5jb25zZW5zdXNfZmVhdHVyZXNdKQogICAgICBmZWF0dXJlX21hdHJpeCA8LSBmZWF0dXJlX21hdHJpeFtjb21wbGV0ZS5jYXNlcyhmZWF0dXJlX21hdHJpeCksIF0KICAgICAgCiAgICAgIGlmKG5yb3coZmVhdHVyZV9tYXRyaXgpID4gMTApIHsKICAgICAgICBjb3JyZWxhdGlvbl9tYXRyaXggPC0gY29yKGZlYXR1cmVfbWF0cml4LCB1c2UgPSAiY29tcGxldGUub2JzIikKICAgICAgICAKICAgICAgICBjb3JfbG9uZyA8LSByZXNoYXBlMjo6bWVsdChjb3JyZWxhdGlvbl9tYXRyaXgpCiAgICAgICAgY29yX2xvbmckVmFyMSA8LSBzdWIoImZlYXR1cmVfIiwgIiIsIGNvcl9sb25nJFZhcjEpCiAgICAgICAgY29yX2xvbmckVmFyMiA8LSBzdWIoImZlYXR1cmVfIiwgIiIsIGNvcl9sb25nJFZhcjIpCiAgICAgICAgCiAgICAgICAgY29ycmVsYXRpb25fcGxvdCA8LSBnZ3Bsb3QoY29yX2xvbmcsIGFlcyh4ID0gVmFyMSwgeSA9IFZhcjIsIGZpbGwgPSB2YWx1ZSkpICsKICAgICAgICAgIGdlb21fdGlsZShjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAwLjUpICsKICAgICAgICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKGxvdyA9ICIjMjE2NkFDIiwgbWlkID0gIndoaXRlIiwgaGlnaCA9ICIjRDczMDI3IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pZHBvaW50ID0gMCwgbmFtZSA9ICJDb3JyZWxhdGlvblxuQ29lZmZpY2llbnQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKC0xLCAxKSkgKwogICAgICAgICAgdGhlbWVfbWluaW1hbCgpICsKICAgICAgICAgIHRoZW1lKAogICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHNpemUgPSAxMCksCiAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCksCiAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwKICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiCiAgICAgICAgICApICsKICAgICAgICAgIGxhYnMoCiAgICAgICAgICAgIHRpdGxlID0gIkMuIEZlYXR1cmUgQ29ycmVsYXRpb24gTWF0cml4IiwKICAgICAgICAgICAgeCA9ICJGZWF0dXJlIElEIiwKICAgICAgICAgICAgeSA9ICJGZWF0dXJlIElEIiwKICAgICAgICAgICAgc3VidGl0bGUgPSAiQ29ycmVsYXRpb24gc3RydWN0dXJlIGFtb25nIHRvcCBwcmVkaWN0aXZlIGZlYXR1cmVzIgogICAgICAgICAgKSArCiAgICAgICAgICBjb29yZF9maXhlZCgpCiAgICAgICAgCiAgICAgICAgcmV0dXJuKGNvcnJlbGF0aW9uX3Bsb3QpCiAgICAgIH0KICAgIH0KICAgIAogICAgZW1wdHlfcGxvdCA8LSBnZ3Bsb3QoKSArIAogICAgICBhbm5vdGF0ZSgidGV4dCIsIHggPSAwLjUsIHkgPSAwLjUsIAogICAgICAgICAgICAgICBsYWJlbCA9ICJJbnN1ZmZpY2llbnQgZGF0YVxuZm9yIGNvcnJlbGF0aW9uIGFuYWx5c2lzIiwgCiAgICAgICAgICAgICAgIHNpemUgPSA2LCBoanVzdCA9IDAuNSkgKwogICAgICB0aGVtZV92b2lkKCkgKwogICAgICBsYWJzKHRpdGxlID0gIkMuIEZlYXR1cmUgQ29ycmVsYXRpb24gTWF0cml4IikgKwogICAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiwgaGp1c3QgPSAwLjUpKQogICAgCiAgICByZXR1cm4oZW1wdHlfcGxvdCkKICB9CiAgCiAgcGFuZWxfYSA8LSBhbmFseXplX2ZlYXR1cmVfY2F0ZWdvcmllcygpCiAgcGFuZWxfYiA8LSBjcmVhdGVfdGlzc3VlX3ZhcmlhYmlsaXR5X2FuYWx5c2lzKCkKICBwYW5lbF9jIDwtIGNyZWF0ZV9mZWF0dXJlX2NvcnJlbGF0aW9uX21hdHJpeCgpCiAgCiAgdG9wX3BhbmVscyA8LSBncmlkLmFycmFuZ2UocGFuZWxfYSwgcGFuZWxfYiwgbmNvbCA9IDIpCiAgZmlndXJlOF9jb21iaW5lZCA8LSBncmlkLmFycmFuZ2UodG9wX3BhbmVscywgcGFuZWxfYywgbnJvdyA9IDIsIGhlaWdodHMgPSBjKDEsIDEpKQogIAogIHJldHVybihmaWd1cmU4X2NvbWJpbmVkKQp9CgphbmFseXplX2Jpb2xvZ2ljYWxfc2lnbmlmaWNhbmNlIDwtIGZ1bmN0aW9uKGFsbF9yZXN1bHRzKSB7CiAgZm9yKHRhcmdldCBpbiBjKCJTTVJJTiIsICJTTUFUU1NDUiIpKSB7CiAgICBhbGxfZmVhdHVyZXMgPC0gdW5saXN0KGxhcHBseShhbGxfcmVzdWx0c1tbdGFyZ2V0XV0sIGZ1bmN0aW9uKHgpIHsKICAgICAgaWYoIWlzLm51bGwoeCRmZWF0dXJlX2NvdW50cykpIG5hbWVzKHgkZmVhdHVyZV9jb3VudHMpIGVsc2UgTlVMTAogICAgfSkpCiAgICAKICAgIGZlYXR1cmVfbnVtcyA8LSBhcy5udW1lcmljKHN1YigiZmVhdHVyZV8iLCAiIiwgYWxsX2ZlYXR1cmVzKSkKICAgIG1lYW5fZmVhdHVyZXMgPC0gc3VtKGZlYXR1cmVfbnVtcyA8PSAxMDI0KQogICAgc3RkX2ZlYXR1cmVzIDwtIHN1bShmZWF0dXJlX251bXMgPiAxMDI0ICYgZmVhdHVyZV9udW1zIDw9IDIwNDgpCiAgICBtaW5fZmVhdHVyZXMgPC0gc3VtKGZlYXR1cmVfbnVtcyA+IDIwNDggJiBmZWF0dXJlX251bXMgPD0gMzA3MikKICAgIG1heF9mZWF0dXJlcyA8LSBzdW0oZmVhdHVyZV9udW1zID4gMzA3MikKICB9Cn0KCmZpZ3VyZThfcGxvdCA8LSBjcmVhdGVfYmlvbG9naWNhbF9pbnRlcnByZXRhdGlvbl9hbmFseXNpcyhhbGxfcmVzdWx0cywgbWVyZ2VkX2d0ZXgpCmFuYWx5emVfYmlvbG9naWNhbF9zaWduaWZpY2FuY2UoYWxsX3Jlc3VsdHMpCgpnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCAiRmlndXJlOF9CaW9sb2dpY2FsX0ludGVycHJldGF0aW9uLnBkZiIpLCAKICAgICAgIGZpZ3VyZThfcGxvdCwgd2lkdGggPSAxNiwgaGVpZ2h0ID0gMTQsIGRwaSA9IDYwMCwgYmcgPSAid2hpdGUiKQpgYGAKCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAxMi4gU3VwcGxlbWVudGFyeSBGaWd1cmUgUzE6IFF1YWxpdHkgU2NvcmUgRGlzdHJpYnV0aW9ucyBieSBUaXNzdWUKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQpgYGB7cn0KY3JlYXRlX3F1YWxpdHlfc2NvcmVfZGlzdHJpYnV0aW9ucyA8LSBmdW5jdGlvbihtZXJnZWRfZ3RleCkgewogIAogIHByZXBhcmVfcXVhbGl0eV9kYXRhIDwtIGZ1bmN0aW9uKCkgewogICAgcmluX2RhdGEgPC0gbWVyZ2VkX2d0ZXhbIWlzLm5hKFNNUklOKSwgLih0aXNzdWVfbWFpbiwgU01SSU4pXQogICAgc2V0bmFtZXMocmluX2RhdGEsICJTTVJJTiIsICJTY29yZSIpCiAgICByaW5fZGF0YSRTY29yZV9UeXBlIDwtICJSSU4gU2NvcmUiCiAgICAKICAgIGF1dG9fZGF0YSA8LSBtZXJnZWRfZ3RleFshaXMubmEoU01BVFNTQ1IpLCAuKHRpc3N1ZV9tYWluLCBTTUFUU1NDUildCiAgICBzZXRuYW1lcyhhdXRvX2RhdGEsICJTTUFUU1NDUiIsICJTY29yZSIpCiAgICBhdXRvX2RhdGEkU2NvcmVfVHlwZSA8LSAiQXV0b2x5c2lzIFNjb3JlIgogICAgCiAgICByZXR1cm4obGlzdChyaW4gPSByaW5fZGF0YSwgYXV0b2x5c2lzID0gYXV0b19kYXRhKSkKICB9CiAgCiAgZ2V0X3RvcF90aXNzdWVzIDwtIGZ1bmN0aW9uKG5fdGlzc3VlcyA9IDIwKSB7CiAgICB0aXNzdWVfY291bnRzIDwtIG1lcmdlZF9ndGV4WywgLk4sIGJ5ID0gdGlzc3VlX21haW5dW29yZGVyKC1OKV0KICAgIHJldHVybihoZWFkKHRpc3N1ZV9jb3VudHMkdGlzc3VlX21haW4sIG5fdGlzc3VlcykpCiAgfQogIAogIHF1YWxpdHlfZGF0YSA8LSBwcmVwYXJlX3F1YWxpdHlfZGF0YSgpCiAgdG9wX3Rpc3N1ZXMgPC0gZ2V0X3RvcF90aXNzdWVzKCkKICAKICByaW5fZmlsdGVyZWQgPC0gcXVhbGl0eV9kYXRhJHJpblt0aXNzdWVfbWFpbiAlaW4lIHRvcF90aXNzdWVzXQogIGF1dG9fZmlsdGVyZWQgPC0gcXVhbGl0eV9kYXRhJGF1dG9seXNpc1t0aXNzdWVfbWFpbiAlaW4lIHRvcF90aXNzdWVzXQogIAogIHJpbl9maWx0ZXJlZCR0aXNzdWVfbWFpbiA8LSBmYWN0b3IocmluX2ZpbHRlcmVkJHRpc3N1ZV9tYWluLCBsZXZlbHMgPSB0b3BfdGlzc3VlcykKICBhdXRvX2ZpbHRlcmVkJHRpc3N1ZV9tYWluIDwtIGZhY3RvcihhdXRvX2ZpbHRlcmVkJHRpc3N1ZV9tYWluLCBsZXZlbHMgPSB0b3BfdGlzc3VlcykKICAKICBjcmVhdGVfcmluX2Rpc3RyaWJ1dGlvbl9wYW5lbCA8LSBmdW5jdGlvbigpIHsKICAgIHJpbl92aW9saW4gPC0gZ2dwbG90KHJpbl9maWx0ZXJlZCwgYWVzKHggPSB0aXNzdWVfbWFpbiwgeSA9IFNjb3JlKSkgKwogICAgICBnZW9tX3Zpb2xpbihmaWxsID0gIiM2NkNEQUEiLCBhbHBoYSA9IDAuOCwgdHJpbSA9IEZBTFNFLCBzY2FsZSA9ICJ3aWR0aCIpICsKICAgICAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4xNSwgYWxwaGEgPSAwLjksIG91dGxpZXIuc2l6ZSA9IDEsIG91dGxpZXIuYWxwaGEgPSAwLjYpICsKICAgICAgc3RhdF9zdW1tYXJ5KGZ1biA9IG1lZGlhbiwgZ2VvbSA9ICJwb2ludCIsIGNvbG9yID0gImRhcmtibHVlIiwgc2l6ZSA9IDEuNSkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICB0aGVtZSgKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHNpemUgPSAxMSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBsb3Quc3VidGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIikKICAgICAgKSArCiAgICAgIGxhYnMoCiAgICAgICAgdGl0bGUgPSAiQS4gUklOIFNjb3JlIERpc3RyaWJ1dGlvbnMgYnkgVGlzc3VlIFR5cGUiLAogICAgICAgIHN1YnRpdGxlID0gIlJOQSBJbnRlZ3JpdHkgTnVtYmVyIGFjcm9zcyBtYWpvciB0aXNzdWUgdHlwZXMiLAogICAgICAgIHggPSBOVUxMLAogICAgICAgIHkgPSAiUklOIFNjb3JlIgogICAgICApICsKICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMSwgMTApLCBicmVha3MgPSBzZXEoMiwgMTAsIDIpKQogICAgCiAgICByZXR1cm4ocmluX3Zpb2xpbikKICB9CiAgCiAgY3JlYXRlX2F1dG9seXNpc19kaXN0cmlidXRpb25fcGFuZWwgPC0gZnVuY3Rpb24oKSB7CiAgICBhdXRvX3Zpb2xpbiA8LSBnZ3Bsb3QoYXV0b19maWx0ZXJlZCwgYWVzKHggPSB0aXNzdWVfbWFpbiwgeSA9IFNjb3JlKSkgKwogICAgICBnZW9tX3Zpb2xpbihmaWxsID0gIiNGQzhENjIiLCBhbHBoYSA9IDAuOCwgdHJpbSA9IEZBTFNFLCBzY2FsZSA9ICJ3aWR0aCIpICsKICAgICAgZ2VvbV9ib3hwbG90KHdpZHRoID0gMC4xNSwgYWxwaGEgPSAwLjksIG91dGxpZXIuc2l6ZSA9IDEsIG91dGxpZXIuYWxwaGEgPSAwLjYpICsKICAgICAgc3RhdF9zdW1tYXJ5KGZ1biA9IG1lZGlhbiwgZ2VvbSA9ICJwb2ludCIsIGNvbG9yID0gImRhcmtyZWQiLCBzaXplID0gMS41KSArCiAgICAgIHRoZW1lX21pbmltYWwoKSArCiAgICAgIHRoZW1lKAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDExLCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKQogICAgICApICsKICAgICAgbGFicygKICAgICAgICB0aXRsZSA9ICJCLiBBdXRvbHlzaXMgU2NvcmUgRGlzdHJpYnV0aW9ucyBieSBUaXNzdWUgVHlwZSIsCiAgICAgICAgc3VidGl0bGUgPSAiVGlzc3VlIGRlZ3JhZGF0aW9uIGxldmVscyBhY3Jvc3MgbWFqb3IgdGlzc3VlIHR5cGVzIiwKICAgICAgICB4ID0gIlRpc3N1ZSBUeXBlIiwKICAgICAgICB5ID0gIkF1dG9seXNpcyBTY29yZSIKICAgICAgKSArCiAgICAgIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDMpLCBicmVha3MgPSAwOjMsCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjAgKE5vbmUpIiwgIjEgKE1pbGQpIiwgIjIgKE1vZGVyYXRlKSIsICIzIChTZXZlcmUpIikpCiAgICAKICAgIHJldHVybihhdXRvX3Zpb2xpbikKICB9CiAgCiAgY2FsY3VsYXRlX2Rpc3RyaWJ1dGlvbl9zdGF0cyA8LSBmdW5jdGlvbigpIHsKICAgIHJpbl9zdGF0cyA8LSByaW5fZmlsdGVyZWRbLCAuKAogICAgICBtZWFuX3Njb3JlID0gcm91bmQobWVhbihTY29yZSwgbmEucm0gPSBUUlVFKSwgMiksCiAgICAgIG1lZGlhbl9zY29yZSA9IHJvdW5kKG1lZGlhbihTY29yZSwgbmEucm0gPSBUUlVFKSwgMiksCiAgICAgIHNkX3Njb3JlID0gcm91bmQoc2QoU2NvcmUsIG5hLnJtID0gVFJVRSksIDIpLAogICAgICBzYW1wbGVfc2l6ZSA9IC5OCiAgICApLCBieSA9IHRpc3N1ZV9tYWluXVtvcmRlcigtbWVhbl9zY29yZSldCiAgICAKICAgIGF1dG9fc3RhdHMgPC0gYXV0b19maWx0ZXJlZFssIC4oCiAgICAgIG1lYW5fc2NvcmUgPSByb3VuZChtZWFuKFNjb3JlLCBuYS5ybSA9IFRSVUUpLCAyKSwKICAgICAgbWVkaWFuX3Njb3JlID0gcm91bmQobWVkaWFuKFNjb3JlLCBuYS5ybSA9IFRSVUUpLCAyKSwKICAgICAgc2Rfc2NvcmUgPSByb3VuZChzZChTY29yZSwgbmEucm0gPSBUUlVFKSwgMiksCiAgICAgIHNhbXBsZV9zaXplID0gLk4KICAgICksIGJ5ID0gdGlzc3VlX21haW5dW29yZGVyKG1lYW5fc2NvcmUpXQogICAgCiAgICByZXR1cm4obGlzdChyaW5fc3RhdHMgPSByaW5fc3RhdHMsIGF1dG9fc3RhdHMgPSBhdXRvX3N0YXRzKSkKICB9CiAgCiAgcGFuZWxfYSA8LSBjcmVhdGVfcmluX2Rpc3RyaWJ1dGlvbl9wYW5lbCgpCiAgcGFuZWxfYiA8LSBjcmVhdGVfYXV0b2x5c2lzX2Rpc3RyaWJ1dGlvbl9wYW5lbCgpCiAgZGlzdHJpYnV0aW9uX3N0YXRzIDwtIGNhbGN1bGF0ZV9kaXN0cmlidXRpb25fc3RhdHMoKQogIAogIHN1cHBfZmlnX3MxIDwtIGdyaWQuYXJyYW5nZShwYW5lbF9hLCBwYW5lbF9iLCBucm93ID0gMikKICAKICByZXR1cm4oc3VwcF9maWdfczEpCn0KCmFuYWx5emVfcXVhbGl0eV9jb3JyZWxhdGlvbnMgPC0gZnVuY3Rpb24obWVyZ2VkX2d0ZXgpIHsKICBxdWFsaXR5X3N1YnNldCA8LSBtZXJnZWRfZ3RleFshaXMubmEoU01SSU4pICYgIWlzLm5hKFNNQVRTU0NSKV0KICAKICBpZihucm93KHF1YWxpdHlfc3Vic2V0KSA+IDEwKSB7CiAgICBjb3JyZWxhdGlvbl90ZXN0IDwtIGNvci50ZXN0KHF1YWxpdHlfc3Vic2V0JFNNUklOLCBxdWFsaXR5X3N1YnNldCRTTUFUU1NDUikKICAgIHJldHVybihjb3JyZWxhdGlvbl90ZXN0KQogIH0gZWxzZSB7CiAgICByZXR1cm4oTlVMTCkKICB9Cn0KCnN1cHBfZmlndXJlX3MxIDwtIGNyZWF0ZV9xdWFsaXR5X3Njb3JlX2Rpc3RyaWJ1dGlvbnMobWVyZ2VkX2d0ZXgpCnF1YWxpdHlfY29ycmVsYXRpb25zIDwtIGFuYWx5emVfcXVhbGl0eV9jb3JyZWxhdGlvbnMobWVyZ2VkX2d0ZXgpCgpnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCAiU3VwcEZpZ19TMV9RdWFsaXR5X0Rpc3RyaWJ1dGlvbnMucGRmIiksIAogICAgICAgc3VwcF9maWd1cmVfczEsIHdpZHRoID0gMTgsIGhlaWdodCA9IDEyLCBkcGkgPSA2MDAsIGJnID0gIndoaXRlIikKYGBgCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgMTMuIFN1cHBsZW1lbnRhcnkgRmlndXJlIFMyOiBDcm9zcy12YWxpZGF0aW9uIFN0YWJpbGl0eSBBbmFseXNpcwojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmBgYHtyfQpjcmVhdGVfY3Zfc3RhYmlsaXR5X2FuYWx5c2lzIDwtIGZ1bmN0aW9uKGFsbF9yZXN1bHRzKSB7CiAgCiAgZXh0cmFjdF9mb2xkX3BlcmZvcm1hbmNlIDwtIGZ1bmN0aW9uKHRhcmdldF9yZXN1bHRzKSB7CiAgICBmb2xkX3BlcmZvcm1hbmNlIDwtIGRhdGEuZnJhbWUoKQogICAgCiAgICBmb3IodGlzc3VlIGluIG5hbWVzKHRhcmdldF9yZXN1bHRzKSkgewogICAgICByZXN1bHQgPC0gdGFyZ2V0X3Jlc3VsdHNbW3Rpc3N1ZV1dCiAgICAgIGlmKCFpcy5udWxsKHJlc3VsdCkgJiYgIWlzLm51bGwocmVzdWx0JGZvbGRfcmVzdWx0cykpIHsKICAgICAgICAKICAgICAgICBmb3IoZm9sZF9yZXN1bHQgaW4gcmVzdWx0JGZvbGRfcmVzdWx0cykgewogICAgICAgICAgZm9sZF9kYXRhIDwtIGRhdGEuZnJhbWUoCiAgICAgICAgICAgIHRpc3N1ZSA9IHRpc3N1ZSwKICAgICAgICAgICAgZm9sZCA9IGZvbGRfcmVzdWx0JGZvbGQsCiAgICAgICAgICAgIHJfdmFsdWUgPSBhYnMoZm9sZF9yZXN1bHQkciksCiAgICAgICAgICAgIHJtc2UgPSBmb2xkX3Jlc3VsdCRybXNlLAogICAgICAgICAgICBtYWUgPSBmb2xkX3Jlc3VsdCRtYWUKICAgICAgICAgICkKICAgICAgICAgIGZvbGRfcGVyZm9ybWFuY2UgPC0gcmJpbmQoZm9sZF9wZXJmb3JtYW5jZSwgZm9sZF9kYXRhKQogICAgICAgIH0KICAgICAgfQogICAgfQogICAgCiAgICByZXR1cm4oZm9sZF9wZXJmb3JtYW5jZSkKICB9CiAgCiAgcmluX2ZvbGRfZGF0YSA8LSBleHRyYWN0X2ZvbGRfcGVyZm9ybWFuY2UoYWxsX3Jlc3VsdHNbWyJTTVJJTiJdXSkKICBhdXRvX2ZvbGRfZGF0YSA8LSBleHRyYWN0X2ZvbGRfcGVyZm9ybWFuY2UoYWxsX3Jlc3VsdHNbWyJTTUFUU1NDUiJdXSkKICAKICByaW5fZm9sZF9kYXRhJHRhcmdldCA8LSAiUklOIgogIGF1dG9fZm9sZF9kYXRhJHRhcmdldCA8LSAiQXV0b2x5c2lzIgogIGNvbWJpbmVkX2ZvbGRfZGF0YSA8LSByYmluZChyaW5fZm9sZF9kYXRhLCBhdXRvX2ZvbGRfZGF0YSkKICAKICBjcmVhdGVfc3RhYmlsaXR5X3NjYXR0ZXIgPC0gZnVuY3Rpb24oKSB7CiAgICBzdGFiaWxpdHlfbWV0cmljcyA8LSBjb21iaW5lZF9mb2xkX2RhdGEgJT4lCiAgICAgIGdyb3VwX2J5KHRpc3N1ZSwgdGFyZ2V0KSAlPiUKICAgICAgc3VtbWFyaXNlKAogICAgICAgIG1lYW5fciA9IG1lYW4ocl92YWx1ZSwgbmEucm0gPSBUUlVFKSwKICAgICAgICBzZF9yID0gc2Qocl92YWx1ZSwgbmEucm0gPSBUUlVFKSwKICAgICAgICBjdl9yID0gc2Qocl92YWx1ZSwgbmEucm0gPSBUUlVFKSAvIG1lYW4ocl92YWx1ZSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAuZ3JvdXBzID0gJ2Ryb3AnCiAgICAgICkgJT4lCiAgICAgIGZpbHRlcihpcy5maW5pdGUoY3ZfcikgJiBjdl9yID4gMCkKICAgIAogICAgc3RhYmlsaXR5X3Bsb3QgPC0gZ2dwbG90KHN0YWJpbGl0eV9tZXRyaWNzLCBhZXMoeCA9IG1lYW5fciwgeSA9IGN2X3IsIGNvbG9yID0gdGFyZ2V0KSkgKwogICAgICBnZW9tX3BvaW50KHNpemUgPSAzLjUsIGFscGhhID0gMC44KSArCiAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gVFJVRSwgYWxwaGEgPSAwLjMpICsKICAgICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIlJJTiIgPSAiIzY2Q0RBQSIsICJBdXRvbHlzaXMiID0gIiNGQzhENjIiKSkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKAogICAgICAgIHRpdGxlID0gIkEuIE1vZGVsIFN0YWJpbGl0eSB2cyBQZXJmb3JtYW5jZSIsCiAgICAgICAgeCA9ICJNZWFuIHxSfCBBY3Jvc3MgRm9sZHMiLAogICAgICAgIHkgPSAiQ29lZmZpY2llbnQgb2YgVmFyaWF0aW9uICh8UnwpIiwKICAgICAgICBjb2xvciA9ICJNb2RlbCBUeXBlIiwKICAgICAgICBzdWJ0aXRsZSA9ICJMb3dlciBDViBpbmRpY2F0ZXMgbW9yZSBzdGFibGUgY3Jvc3MtdmFsaWRhdGlvbiBwZXJmb3JtYW5jZSIKICAgICAgKSArCiAgICAgIHRoZW1lKAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpCiAgICAgICkKICAgIAogICAgcmV0dXJuKHN0YWJpbGl0eV9wbG90KQogIH0KICAKICBjcmVhdGVfZm9sZF92YXJpYWJpbGl0eV9ib3hwbG90cyA8LSBmdW5jdGlvbigpIHsKICAgIHRvcF90aXNzdWVzX3JpbiA8LSBjb21iaW5lZF9mb2xkX2RhdGEgJT4lIAogICAgICBmaWx0ZXIodGFyZ2V0ID09ICJSSU4iKSAlPiUKICAgICAgZ3JvdXBfYnkodGlzc3VlKSAlPiUKICAgICAgc3VtbWFyaXNlKG1lYW5fciA9IG1lYW4ocl92YWx1ZSwgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lCiAgICAgIGFycmFuZ2UoZGVzYyhtZWFuX3IpKSAlPiUgCiAgICAgIHNsaWNlKDE6OCkgJT4lIAogICAgICBwdWxsKHRpc3N1ZSkKICAgIAogICAgdG9wX3Rpc3N1ZXNfYXV0byA8LSBjb21iaW5lZF9mb2xkX2RhdGEgJT4lIAogICAgICBmaWx0ZXIodGFyZ2V0ID09ICJBdXRvbHlzaXMiKSAlPiUKICAgICAgZ3JvdXBfYnkodGlzc3VlKSAlPiUKICAgICAgc3VtbWFyaXNlKG1lYW5fciA9IG1lYW4ocl92YWx1ZSwgbmEucm0gPSBUUlVFKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lCiAgICAgIGFycmFuZ2UoZGVzYyhtZWFuX3IpKSAlPiUgCiAgICAgIHNsaWNlKDE6OCkgJT4lIAogICAgICBwdWxsKHRpc3N1ZSkKICAgIAogICAgZm9sZF9zdWJzZXQgPC0gY29tYmluZWRfZm9sZF9kYXRhICU+JQogICAgICBmaWx0ZXIoKHRhcmdldCA9PSAiUklOIiAmIHRpc3N1ZSAlaW4lIHRvcF90aXNzdWVzX3JpbikgfAogICAgICAgICAgICAgKHRhcmdldCA9PSAiQXV0b2x5c2lzIiAmIHRpc3N1ZSAlaW4lIHRvcF90aXNzdWVzX2F1dG8pKQogICAgCiAgICB2YXJpYWJpbGl0eV9wbG90IDwtIGdncGxvdChmb2xkX3N1YnNldCwgYWVzKHggPSB0aXNzdWUsIHkgPSByX3ZhbHVlLCBmaWxsID0gdGFyZ2V0KSkgKwogICAgICBnZW9tX2JveHBsb3QoYWxwaGEgPSAwLjgsIG91dGxpZXIuYWxwaGEgPSAwLjYpICsKICAgICAgZmFjZXRfd3JhcCh+dGFyZ2V0LCBzY2FsZXMgPSAiZnJlZV94IiwgbmNvbCA9IDEpICsKICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiUklOIiA9ICIjNjZDREFBIiwgIkF1dG9seXNpcyIgPSAiI0ZDOEQ2MiIpKSArCiAgICAgIHRoZW1lX21pbmltYWwoKSArCiAgICAgIHRoZW1lKAogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSwgc2l6ZSA9IDEwKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIikKICAgICAgKSArCiAgICAgIGxhYnMoCiAgICAgICAgdGl0bGUgPSAiQi4gQ3Jvc3MtRm9sZCBQZXJmb3JtYW5jZSBWYXJpYWJpbGl0eSIsCiAgICAgICAgeCA9ICJUaXNzdWUgVHlwZSIsCiAgICAgICAgeSA9ICJ8UnwgVmFsdWUgQWNyb3NzIEZvbGRzIiwKICAgICAgICBzdWJ0aXRsZSA9ICJCb3ggcGxvdHMgc2hvdyBkaXN0cmlidXRpb24gb2YgcGVyZm9ybWFuY2UgYWNyb3NzIENWIGZvbGRzIgogICAgICApCiAgICAKICAgIHJldHVybih2YXJpYWJpbGl0eV9wbG90KQogIH0KICAKICBjcmVhdGVfc3RhYmlsaXR5X3JhbmtpbmcgPC0gZnVuY3Rpb24oKSB7CiAgICBzdGFiaWxpdHlfcmFua2luZyA8LSBjb21iaW5lZF9mb2xkX2RhdGEgJT4lCiAgICAgIGdyb3VwX2J5KHRpc3N1ZSwgdGFyZ2V0KSAlPiUKICAgICAgc3VtbWFyaXNlKAogICAgICAgIG1lYW5fciA9IG1lYW4ocl92YWx1ZSwgbmEucm0gPSBUUlVFKSwKICAgICAgICBjdl9yID0gc2Qocl92YWx1ZSwgbmEucm0gPSBUUlVFKSAvIG1lYW4ocl92YWx1ZSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAuZ3JvdXBzID0gJ2Ryb3AnCiAgICAgICkgJT4lCiAgICAgIGZpbHRlcihpcy5maW5pdGUoY3ZfcikgJiBjdl9yID4gMCkgJT4lCiAgICAgIGFycmFuZ2UoY3ZfcikgJT4lCiAgICAgIG11dGF0ZSgKICAgICAgICBzdGFiaWxpdHlfcmFuayA9IHJvd19udW1iZXIoKSwKICAgICAgICBzdGFiaWxpdHlfY2F0ZWdvcnkgPSBjdXQoY3ZfciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDAsIDAuMSwgMC4yLCAwLjUsIEluZiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJWZXJ5IFN0YWJsZSIsICJTdGFibGUiLCAiTW9kZXJhdGUiLCAiVW5zdGFibGUiKSkKICAgICAgKQogICAgCiAgICByYW5raW5nX3Bsb3QgPC0gZ2dwbG90KHN0YWJpbGl0eV9yYW5raW5nLCBhZXMoeCA9IHN0YWJpbGl0eV9yYW5rLCB5ID0gY3ZfciwgY29sb3IgPSB0YXJnZXQpKSArCiAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDMsIGFscGhhID0gMC44KSArCiAgICAgIGdlb21faGxpbmUoeWludGVyY2VwdCA9IGMoMC4xLCAwLjIsIDAuNSksIGxpbmV0eXBlID0gImRhc2hlZCIsIGFscGhhID0gMC42KSArCiAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJSSU4iID0gIiM2NkNEQUEiLCAiQXV0b2x5c2lzIiA9ICIjRkM4RDYyIikpICsKICAgICAgdGhlbWVfbWluaW1hbCgpICsKICAgICAgbGFicygKICAgICAgICB0aXRsZSA9ICJDLiBNb2RlbCBTdGFiaWxpdHkgUmFua2luZyIsCiAgICAgICAgeCA9ICJTdGFiaWxpdHkgUmFuayAoMSA9IE1vc3QgU3RhYmxlKSIsCiAgICAgICAgeSA9ICJDb2VmZmljaWVudCBvZiBWYXJpYXRpb24gKHxSfCkiLAogICAgICAgIGNvbG9yID0gIk1vZGVsIFR5cGUiLAogICAgICAgIHN1YnRpdGxlID0gIkhvcml6b250YWwgbGluZXMgaW5kaWNhdGUgc3RhYmlsaXR5IHRocmVzaG9sZHMiCiAgICAgICkgKwogICAgICB0aGVtZSgKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIikKICAgICAgKQogICAgCiAgICByZXR1cm4ocmFua2luZ19wbG90KQogIH0KICAKICBwYW5lbF9hIDwtIGNyZWF0ZV9zdGFiaWxpdHlfc2NhdHRlcigpCiAgcGFuZWxfYiA8LSBjcmVhdGVfZm9sZF92YXJpYWJpbGl0eV9ib3hwbG90cygpCiAgcGFuZWxfYyA8LSBjcmVhdGVfc3RhYmlsaXR5X3JhbmtpbmcoKQogIAogIHRvcF9wYW5lbHMgPC0gZ3JpZC5hcnJhbmdlKHBhbmVsX2EsIHBhbmVsX2MsIG5jb2wgPSAyKQogIHN1cHBfZmlnX3MyIDwtIGdyaWQuYXJyYW5nZSh0b3BfcGFuZWxzLCBwYW5lbF9iLCBucm93ID0gMiwgaGVpZ2h0cyA9IGMoMSwgMS41KSkKICAKICByZXR1cm4oc3VwcF9maWdfczIpCn0KCmNhbGN1bGF0ZV9zdGFiaWxpdHlfc3RhdHMgPC0gZnVuY3Rpb24oYWxsX3Jlc3VsdHMpIHsKICBzdGFiaWxpdHlfc3VtbWFyeSA8LSBkYXRhLmZyYW1lKCkKICAKICBmb3IodGFyZ2V0IGluIGMoIlNNUklOIiwgIlNNQVRTU0NSIikpIHsKICAgIGZvcih0aXNzdWUgaW4gbmFtZXMoYWxsX3Jlc3VsdHNbW3RhcmdldF1dKSkgewogICAgICByZXN1bHQgPC0gYWxsX3Jlc3VsdHNbW3RhcmdldF1dW1t0aXNzdWVdXQogICAgICBpZighaXMubnVsbChyZXN1bHQpICYmICFpcy5udWxsKHJlc3VsdCRmb2xkX3Jlc3VsdHMpKSB7CiAgICAgICAgCiAgICAgICAgZm9sZF9yX3ZhbHVlcyA8LSBzYXBwbHkocmVzdWx0JGZvbGRfcmVzdWx0cywgZnVuY3Rpb24oeCkgYWJzKHgkcikpCiAgICAgICAgCiAgICAgICAgc3RhYmlsaXR5X3JvdyA8LSBkYXRhLmZyYW1lKAogICAgICAgICAgdGlzc3VlID0gdGlzc3VlLAogICAgICAgICAgdGFyZ2V0ID0gdGFyZ2V0LAogICAgICAgICAgbWVhbl9yID0gbWVhbihmb2xkX3JfdmFsdWVzKSwKICAgICAgICAgIGN2X3IgPSBzZChmb2xkX3JfdmFsdWVzKSAvIG1lYW4oZm9sZF9yX3ZhbHVlcyksCiAgICAgICAgICBtaW5fciA9IG1pbihmb2xkX3JfdmFsdWVzKSwKICAgICAgICAgIG1heF9yID0gbWF4KGZvbGRfcl92YWx1ZXMpCiAgICAgICAgKQogICAgICAgIHN0YWJpbGl0eV9zdW1tYXJ5IDwtIHJiaW5kKHN0YWJpbGl0eV9zdW1tYXJ5LCBzdGFiaWxpdHlfcm93KQogICAgICB9CiAgICB9CiAgfQogIAogIGJ5X3RhcmdldCA8LSBzdGFiaWxpdHlfc3VtbWFyeSAlPiUKICAgIGdyb3VwX2J5KHRhcmdldCkgJT4lCiAgICBzdW1tYXJpc2UoCiAgICAgIG1lYW5fY3YgPSByb3VuZChtZWFuKGN2X3IsIG5hLnJtID0gVFJVRSksIDMpLAogICAgICBtZWRpYW5fY3YgPSByb3VuZChtZWRpYW4oY3ZfciwgbmEucm0gPSBUUlVFKSwgMyksCiAgICAgIHN0YWJsZV9tb2RlbHMgPSBzdW0oY3ZfciA8IDAuMiwgbmEucm0gPSBUUlVFKSwKICAgICAgdG90YWxfbW9kZWxzID0gbigpLAogICAgICAuZ3JvdXBzID0gJ2Ryb3AnCiAgICApCiAgCiAgcmV0dXJuKHN0YWJpbGl0eV9zdW1tYXJ5KQp9CgpzdXBwX2ZpZ3VyZV9zMiA8LSBjcmVhdGVfY3Zfc3RhYmlsaXR5X2FuYWx5c2lzKGFsbF9yZXN1bHRzKQpzdGFiaWxpdHlfc3RhdHMgPC0gY2FsY3VsYXRlX3N0YWJpbGl0eV9zdGF0cyhhbGxfcmVzdWx0cykKCmdnc2F2ZShmaWxlLnBhdGgob3V0cHV0X3BhdGgsICJTdXBwRmlnX1MyX0NWX1N0YWJpbGl0eS5wZGYiKSwgCiAgICAgICBzdXBwX2ZpZ3VyZV9zMiwgd2lkdGggPSAxNCwgaGVpZ2h0ID0gMTIsIGRwaSA9IDYwMCwgYmcgPSAid2hpdGUiKQpgYGAKCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgMTQuIFN1cHBsZW1lbnRhcnkgRmlndXJlIFMzOiBFeHRlbmRlZCBDb21wYXJhdGl2ZSBBbmFseXNpcwojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmBgYHtyfQpjcmVhdGVfZXh0ZW5kZWRfY29tcGFyYXRpdmVfYW5hbHlzaXMgPC0gZnVuY3Rpb24oYWxsX3Jlc3VsdHMsIHJpbl9zdW1tYXJ5LCBhdXRvbHlzaXNfc3VtbWFyeSkgewogIAogIGNyZWF0ZV9jcm9zc19tb2RlbF9jb3JyZWxhdGlvbiA8LSBmdW5jdGlvbigpIHsKICAgIGNvbW1vbl90aXNzdWVzIDwtIGludGVyc2VjdChyaW5fc3VtbWFyeSRUaXNzdWUsIGF1dG9seXNpc19zdW1tYXJ5JFRpc3N1ZSkKICAgIAogICAgY29tcGFyaXNvbl9kYXRhIDwtIGRhdGEuZnJhbWUoCiAgICAgIFRpc3N1ZSA9IGNvbW1vbl90aXNzdWVzLAogICAgICBSSU5fUiA9IHJpbl9zdW1tYXJ5JFJbbWF0Y2goY29tbW9uX3Rpc3N1ZXMsIHJpbl9zdW1tYXJ5JFRpc3N1ZSldLAogICAgICBBdXRvX1IgPSBhdXRvbHlzaXNfc3VtbWFyeSRSW21hdGNoKGNvbW1vbl90aXNzdWVzLCBhdXRvbHlzaXNfc3VtbWFyeSRUaXNzdWUpXSwKICAgICAgUklOX1JNU0UgPSByaW5fc3VtbWFyeSRSTVNFW21hdGNoKGNvbW1vbl90aXNzdWVzLCByaW5fc3VtbWFyeSRUaXNzdWUpXSwKICAgICAgQXV0b19STVNFID0gYXV0b2x5c2lzX3N1bW1hcnkkUk1TRVttYXRjaChjb21tb25fdGlzc3VlcywgYXV0b2x5c2lzX3N1bW1hcnkkVGlzc3VlKV0KICAgICkKICAgIAogICAgcl9jb3JyZWxhdGlvbiA8LSBjb3IoYWJzKGNvbXBhcmlzb25fZGF0YSRSSU5fUiksIGFicyhjb21wYXJpc29uX2RhdGEkQXV0b19SKSwgdXNlID0gImNvbXBsZXRlLm9icyIpCiAgICAKICAgIGNvcnJlbGF0aW9uX3Bsb3QgPC0gZ2dwbG90KGNvbXBhcmlzb25fZGF0YSwgYWVzKHggPSBhYnMoUklOX1IpLCB5ID0gYWJzKEF1dG9fUikpKSArCiAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDMuNSwgYWxwaGEgPSAwLjgsIGNvbG9yID0gImRhcmtibHVlIikgKwogICAgICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IFRSVUUsIGNvbG9yID0gInJlZCIsIGFscGhhID0gMC4zKSArCiAgICAgIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAiZ3JheSIsIHNpemUgPSAxKSArCiAgICAgIHRoZW1lX21pbmltYWwoKSArCiAgICAgIGxhYnMoCiAgICAgICAgdGl0bGUgPSAiQS4gQ3Jvc3MtTW9kZWwgUGVyZm9ybWFuY2UgQ29ycmVsYXRpb24iLAogICAgICAgIHggPSAiUklOIE1vZGVsIHxSfCIsCiAgICAgICAgeSA9ICJBdXRvbHlzaXMgTW9kZWwgfFJ8IiwKICAgICAgICBzdWJ0aXRsZSA9IHBhc3RlKCJDb3JyZWxhdGlvbiA9Iiwgcm91bmQocl9jb3JyZWxhdGlvbiwgMykpCiAgICAgICkgKwogICAgICB0aGVtZSgKICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLAogICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKQogICAgICApICsKICAgICAgY29vcmRfZml4ZWQoKQogICAgCiAgICByZXR1cm4oY29ycmVsYXRpb25fcGxvdCkKICB9CiAgCiAgY3JlYXRlX3NhbXBsZV9zaXplX3BlcmZvcm1hbmNlIDwtIGZ1bmN0aW9uKCkgewogICAgY29tYmluZWRfZGF0YSA8LSByYmluZCgKICAgICAgZGF0YS5mcmFtZShyaW5fc3VtbWFyeVssIGMoIlRpc3N1ZSIsICJTYW1wbGVzIiwgIlIiKV0sIE1vZGVsID0gIlJJTiIpLAogICAgICBkYXRhLmZyYW1lKGF1dG9seXNpc19zdW1tYXJ5WywgYygiVGlzc3VlIiwgIlNhbXBsZXMiLCAiUiIpXSwgTW9kZWwgPSAiQXV0b2x5c2lzIikKICAgICkKICAgIAogICAgY29tYmluZWRfZGF0YSRSX2FicyA8LSBhYnMoY29tYmluZWRfZGF0YSRSKQogICAgY29tYmluZWRfZGF0YSRTaXplX0JpbiA8LSBjdXQoY29tYmluZWRfZGF0YSRTYW1wbGVzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygwLCA1MCwgMTAwLCAyMDAsIDUwMCwgSW5mKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygi4omkNTAiLCAiNTEtMTAwIiwgIjEwMS0yMDAiLCAiMjAxLTUwMCIsICI+NTAwIikpCiAgICAKICAgIHNpemVfcGVyZm9ybWFuY2VfcGxvdCA8LSBnZ3Bsb3QoY29tYmluZWRfZGF0YSwgYWVzKHggPSBTaXplX0JpbiwgeSA9IFJfYWJzLCBmaWxsID0gTW9kZWwpKSArCiAgICAgIGdlb21fYm94cGxvdChhbHBoYSA9IDAuOCwgb3V0bGllci5hbHBoYSA9IDAuNikgKwogICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJSSU4iID0gIiM2NkNEQUEiLCAiQXV0b2x5c2lzIiA9ICIjRkM4RDYyIikpICsKICAgICAgdGhlbWVfbWluaW1hbCgpICsKICAgICAgbGFicygKICAgICAgICB0aXRsZSA9ICJCLiBNb2RlbCBQZXJmb3JtYW5jZSBieSBTYW1wbGUgU2l6ZSBDYXRlZ29yeSIsCiAgICAgICAgeCA9ICJTYW1wbGUgU2l6ZSBDYXRlZ29yeSIsCiAgICAgICAgeSA9ICJBYnNvbHV0ZSBDb3JyZWxhdGlvbiBDb2VmZmljaWVudCB8UnwiLAogICAgICAgIHN1YnRpdGxlID0gIlBlcmZvcm1hbmNlIGRpc3RyaWJ1dGlvbiBhY3Jvc3Mgc2FtcGxlIHNpemUgYmlucyIKICAgICAgKSArCiAgICAgIHRoZW1lKAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpCiAgICAgICkKICAgIAogICAgcmV0dXJuKHNpemVfcGVyZm9ybWFuY2VfcGxvdCkKICB9CiAgCiAgY3JlYXRlX3Rpc3N1ZV9wcmVmZXJlbmNlX2FuYWx5c2lzIDwtIGZ1bmN0aW9uKCkgewogICAgY29tbW9uX3Rpc3N1ZXMgPC0gaW50ZXJzZWN0KHJpbl9zdW1tYXJ5JFRpc3N1ZSwgYXV0b2x5c2lzX3N1bW1hcnkkVGlzc3VlKQogICAgCiAgICBwcmVmZXJlbmNlX2RhdGEgPC0gZGF0YS5mcmFtZSgKICAgICAgVGlzc3VlID0gY29tbW9uX3Rpc3N1ZXMsCiAgICAgIFJJTl9SID0gYWJzKHJpbl9zdW1tYXJ5JFJbbWF0Y2goY29tbW9uX3Rpc3N1ZXMsIHJpbl9zdW1tYXJ5JFRpc3N1ZSldKSwKICAgICAgQXV0b19SID0gYWJzKGF1dG9seXNpc19zdW1tYXJ5JFJbbWF0Y2goY29tbW9uX3Rpc3N1ZXMsIGF1dG9seXNpc19zdW1tYXJ5JFRpc3N1ZSldKQogICAgKQogICAgCiAgICBwcmVmZXJlbmNlX2RhdGEkQmV0dGVyX01vZGVsIDwtIGlmZWxzZSgKICAgICAgcHJlZmVyZW5jZV9kYXRhJFJJTl9SID4gcHJlZmVyZW5jZV9kYXRhJEF1dG9fUiwgIlJJTiIsICJBdXRvbHlzaXMiCiAgICApCiAgICBwcmVmZXJlbmNlX2RhdGEkUGVyZm9ybWFuY2VfRGlmZmVyZW5jZSA8LSBhYnMocHJlZmVyZW5jZV9kYXRhJFJJTl9SIC0gcHJlZmVyZW5jZV9kYXRhJEF1dG9fUikKICAgIHByZWZlcmVuY2VfZGF0YSRNYXhfUiA8LSBwbWF4KHByZWZlcmVuY2VfZGF0YSRSSU5fUiwgcHJlZmVyZW5jZV9kYXRhJEF1dG9fUikKICAgIAogICAgcHJlZmVyZW5jZV9kYXRhJFRpc3N1ZV9TeXN0ZW0gPC0gY2FzZV93aGVuKAogICAgICBncmVwbCgiQnJhaW58TmVydmV8U3BpbmFsIiwgcHJlZmVyZW5jZV9kYXRhJFRpc3N1ZSkgfiAiTmVydm91cyIsCiAgICAgIGdyZXBsKCJIZWFydHxBcnRlcnl8QW9ydGEiLCBwcmVmZXJlbmNlX2RhdGEkVGlzc3VlKSB+ICJDYXJkaW92YXNjdWxhciIsIAogICAgICBncmVwbCgiTHVuZ3xUcmFjaGVhIiwgcHJlZmVyZW5jZV9kYXRhJFRpc3N1ZSkgfiAiUmVzcGlyYXRvcnkiLAogICAgICBncmVwbCgiTGl2ZXJ8UGFuY3JlYXN8U3RvbWFjaHxDb2xvbnxTbWFsbHxFc29waGFndXMiLCBwcmVmZXJlbmNlX2RhdGEkVGlzc3VlKSB+ICJEaWdlc3RpdmUiLAogICAgICBncmVwbCgiS2lkbmV5fEJsYWRkZXIiLCBwcmVmZXJlbmNlX2RhdGEkVGlzc3VlKSB+ICJVcmluYXJ5IiwKICAgICAgZ3JlcGwoIk11c2NsZXxTa2luIiwgcHJlZmVyZW5jZV9kYXRhJFRpc3N1ZSkgfiAiTXVzY3Vsb3NrZWxldGFsIiwKICAgICAgZ3JlcGwoIlRoeXJvaWR8QWRyZW5hbHxQaXR1aXRhcnkiLCBwcmVmZXJlbmNlX2RhdGEkVGlzc3VlKSB+ICJFbmRvY3JpbmUiLAogICAgICBUUlVFIH4gIk90aGVyIgogICAgKQogICAgCiAgICBwcmVmZXJlbmNlX3Bsb3QgPC0gZ2dwbG90KHByZWZlcmVuY2VfZGF0YSwgYWVzKHggPSBNYXhfUiwgeSA9IFBlcmZvcm1hbmNlX0RpZmZlcmVuY2UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gQmV0dGVyX01vZGVsLCBzaGFwZSA9IFRpc3N1ZV9TeXN0ZW0pKSArCiAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDQsIGFscGhhID0gMC44KSArCiAgICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJSSU4iID0gIiM2NkNEQUEiLCAiQXV0b2x5c2lzIiA9ICIjRkM4RDYyIikpICsKICAgICAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMTYsIDE3LCAxOCwgMTUsIDMsIDQsIDgsIDEpWzE6bGVuZ3RoKHVuaXF1ZShwcmVmZXJlbmNlX2RhdGEkVGlzc3VlX1N5c3RlbSkpXSkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKAogICAgICAgIHRpdGxlID0gIkMuIFRpc3N1ZSBDYXRlZ29yaWVzIGFuZCBNb2RlbCBQcmVmZXJlbmNlIiwKICAgICAgICB4ID0gIk1heGltdW0gfFJ8IFZhbHVlIiwKICAgICAgICB5ID0gIkFic29sdXRlIERpZmZlcmVuY2UgaW4gfFJ8IiwKICAgICAgICBjb2xvciA9ICJCZXR0ZXIgTW9kZWwiLAogICAgICAgIHNoYXBlID0gIlRpc3N1ZSBTeXN0ZW0iLAogICAgICAgIHN1YnRpdGxlID0gIk1vZGVsIHByZWZlcmVuY2UgcGF0dGVybnMgYWNyb3NzIGJpb2xvZ2ljYWwgc3lzdGVtcyIKICAgICAgKSArCiAgICAgIHRoZW1lKAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIikKICAgICAgKQogICAgCiAgICByZXR1cm4ocHJlZmVyZW5jZV9wbG90KQogIH0KICAKICBjcmVhdGVfZmVhdHVyZV9vdmVybGFwX2FuYWx5c2lzIDwtIGZ1bmN0aW9uKCkgewogICAgZXh0cmFjdF9mZWF0dXJlcyA8LSBmdW5jdGlvbih0YXJnZXRfcmVzdWx0cywgdGhyZXNob2xkID0gNSkgewogICAgICBmZWF0dXJlX2NvdW50cyA8LSBsaXN0KCkKICAgICAgZm9yKHRpc3N1ZSBpbiBuYW1lcyh0YXJnZXRfcmVzdWx0cykpIHsKICAgICAgICBpZighaXMubnVsbCh0YXJnZXRfcmVzdWx0c1tbdGlzc3VlXV0kZmVhdHVyZV9jb3VudHMpKSB7CiAgICAgICAgICBjb3VudHMgPC0gdGFyZ2V0X3Jlc3VsdHNbW3Rpc3N1ZV1dJGZlYXR1cmVfY291bnRzCiAgICAgICAgICBmb3IoZmVhdCBpbiBuYW1lcyhjb3VudHMpKSB7CiAgICAgICAgICAgIGlmKGlzLm51bGwoZmVhdHVyZV9jb3VudHNbW2ZlYXRdXSkpIGZlYXR1cmVfY291bnRzW1tmZWF0XV0gPC0gMAogICAgICAgICAgICBmZWF0dXJlX2NvdW50c1tbZmVhdF1dIDwtIGZlYXR1cmVfY291bnRzW1tmZWF0XV0gKyBjb3VudHNbW2ZlYXRdXQogICAgICAgICAgfQogICAgICAgIH0KICAgICAgfQogICAgICAKICAgICAgaW1wb3J0YW50X2ZlYXR1cmVzIDwtIG5hbWVzKGZlYXR1cmVfY291bnRzKVtmZWF0dXJlX2NvdW50cyA+PSB0aHJlc2hvbGRdCiAgICAgIHJldHVybihpbXBvcnRhbnRfZmVhdHVyZXMpCiAgICB9CiAgICAKICAgIHRocmVzaG9sZHMgPC0gYygzLCA1LCAxMCwgMTUsIDIwKQogICAgb3ZlcmxhcF9kYXRhIDwtIGRhdGEuZnJhbWUoKQogICAgCiAgICBmb3IodGhyZXNoIGluIHRocmVzaG9sZHMpIHsKICAgICAgcmluX3RocmVzaCA8LSBleHRyYWN0X2ZlYXR1cmVzKGFsbF9yZXN1bHRzW1siU01SSU4iXV0sIHRocmVzaCkKICAgICAgYXV0b190aHJlc2ggPC0gZXh0cmFjdF9mZWF0dXJlcyhhbGxfcmVzdWx0c1tbIlNNQVRTU0NSIl1dLCB0aHJlc2gpCiAgICAgIAogICAgICBvdmVybGFwX2NvdW50IDwtIGxlbmd0aChpbnRlcnNlY3QocmluX3RocmVzaCwgYXV0b190aHJlc2gpKQogICAgICByaW5fb25seSA8LSBsZW5ndGgoc2V0ZGlmZihyaW5fdGhyZXNoLCBhdXRvX3RocmVzaCkpCiAgICAgIGF1dG9fb25seSA8LSBsZW5ndGgoc2V0ZGlmZihhdXRvX3RocmVzaCwgcmluX3RocmVzaCkpCiAgICAgIAogICAgICBvdmVybGFwX2RhdGEgPC0gcmJpbmQob3ZlcmxhcF9kYXRhLCBkYXRhLmZyYW1lKAogICAgICAgIFRocmVzaG9sZCA9IHRocmVzaCwKICAgICAgICBPdmVybGFwID0gb3ZlcmxhcF9jb3VudCwKICAgICAgICBSSU5fT25seSA9IHJpbl9vbmx5LAogICAgICAgIEF1dG9fT25seSA9IGF1dG9fb25seQogICAgICApKQogICAgfQogICAgCiAgICBvdmVybGFwX2xvbmcgPC0gcmVzaGFwZTI6Om1lbHQob3ZlcmxhcF9kYXRhLCBpZC52YXJzID0gIlRocmVzaG9sZCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUubmFtZSA9ICJDYXRlZ29yeSIsIHZhbHVlLm5hbWUgPSAiQ291bnQiKQogICAgCiAgICBvdmVybGFwX3Bsb3QgPC0gZ2dwbG90KG92ZXJsYXBfbG9uZywgYWVzKHggPSBmYWN0b3IoVGhyZXNob2xkKSwgeSA9IENvdW50LCBmaWxsID0gQ2F0ZWdvcnkpKSArCiAgICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJzdGFjayIpICsKICAgICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiT3ZlcmxhcCIgPSAiIzhFNDRBRCIsICJSSU5fT25seSIgPSAiIzY2Q0RBQSIsICJBdXRvX09ubHkiID0gIiNGQzhENjIiKSwKICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJDb21tb24gRmVhdHVyZXMiLCAiUklOIE9ubHkiLCAiQXV0b2x5c2lzIE9ubHkiKSkgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICBsYWJzKAogICAgICAgIHRpdGxlID0gIkQuIEZlYXR1cmUgT3ZlcmxhcCBhdCBEaWZmZXJlbnQgU2VsZWN0aW9uIFRocmVzaG9sZHMiLAogICAgICAgIHggPSAiTWluaW11bSBTZWxlY3Rpb24gRnJlcXVlbmN5IiwKICAgICAgICB5ID0gIk51bWJlciBvZiBGZWF0dXJlcyIsCiAgICAgICAgZmlsbCA9ICJGZWF0dXJlIFR5cGUiLAogICAgICAgIHN1YnRpdGxlID0gIk92ZXJsYXAgYW5hbHlzaXMgYWNyb3NzIHNlbGVjdGlvbiBmcmVxdWVuY3kgdGhyZXNob2xkcyIKICAgICAgKSArCiAgICAgIHRoZW1lKAogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2LCBmYWNlID0gImJvbGQiKSwKICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpCiAgICAgICkKICAgIAogICAgcmV0dXJuKG92ZXJsYXBfcGxvdCkKICB9CiAgCiAgcGFuZWxfYSA8LSBjcmVhdGVfY3Jvc3NfbW9kZWxfY29ycmVsYXRpb24oKQogIHBhbmVsX2IgPC0gY3JlYXRlX3NhbXBsZV9zaXplX3BlcmZvcm1hbmNlKCkKICBwYW5lbF9jIDwtIGNyZWF0ZV90aXNzdWVfcHJlZmVyZW5jZV9hbmFseXNpcygpCiAgcGFuZWxfZCA8LSBjcmVhdGVfZmVhdHVyZV9vdmVybGFwX2FuYWx5c2lzKCkKICAKICBjb21iaW5lZF9wbG90IDwtIGdyaWQuYXJyYW5nZShwYW5lbF9hLCBwYW5lbF9iLCBwYW5lbF9jLCBwYW5lbF9kLCBucm93ID0gMiwgbmNvbCA9IDIpCiAgCiAgcmV0dXJuKGNvbWJpbmVkX3Bsb3QpCn0KCmNyZWF0ZV9tb2RlbF9jb21wYXJpc29uX3N1bW1hcnkgPC0gZnVuY3Rpb24ocmluX3N1bW1hcnksIGF1dG9seXNpc19zdW1tYXJ5KSB7CiAgY29tbW9uX3Rpc3N1ZXMgPC0gaW50ZXJzZWN0KHJpbl9zdW1tYXJ5JFRpc3N1ZSwgYXV0b2x5c2lzX3N1bW1hcnkkVGlzc3VlKQogIAogIGNvbXBhcmlzb25fc3VtbWFyeSA8LSBkYXRhLmZyYW1lKAogICAgVGlzc3VlID0gY29tbW9uX3Rpc3N1ZXMsCiAgICBSSU5fUiA9IGFicyhyaW5fc3VtbWFyeSRSW21hdGNoKGNvbW1vbl90aXNzdWVzLCByaW5fc3VtbWFyeSRUaXNzdWUpXSksCiAgICBBdXRvX1IgPSBhYnMoYXV0b2x5c2lzX3N1bW1hcnkkUlttYXRjaChjb21tb25fdGlzc3VlcywgYXV0b2x5c2lzX3N1bW1hcnkkVGlzc3VlKV0pCiAgKQogIAogIGNvbXBhcmlzb25fc3VtbWFyeSRCZXR0ZXJfTW9kZWwgPC0gaWZlbHNlKAogICAgY29tcGFyaXNvbl9zdW1tYXJ5JFJJTl9SID4gY29tcGFyaXNvbl9zdW1tYXJ5JEF1dG9fUiwgIlJJTiIsICJBdXRvbHlzaXMiCiAgKQogIAogIG1vZGVsX3ByZWZlcmVuY2UgPC0gdGFibGUoY29tcGFyaXNvbl9zdW1tYXJ5JEJldHRlcl9Nb2RlbCkKICAKICByZXR1cm4obGlzdCgKICAgIHN1bW1hcnkgPSBjb21wYXJpc29uX3N1bW1hcnksCiAgICBwcmVmZXJlbmNlX2NvdW50cyA9IG1vZGVsX3ByZWZlcmVuY2UsCiAgICBtZWFuX3BlcmZvcm1hbmNlID0gYygKICAgICAgUklOID0gbWVhbihjb21wYXJpc29uX3N1bW1hcnkkUklOX1IsIG5hLnJtID0gVFJVRSksCiAgICAgIEF1dG9seXNpcyA9IG1lYW4oY29tcGFyaXNvbl9zdW1tYXJ5JEF1dG9fUiwgbmEucm0gPSBUUlVFKQogICAgKQogICkpCn0KCnN1cHBfZmlndXJlX3MzIDwtIGNyZWF0ZV9leHRlbmRlZF9jb21wYXJhdGl2ZV9hbmFseXNpcyhhbGxfcmVzdWx0cywgcmluX3N1bW1hcnksIGF1dG9seXNpc19zdW1tYXJ5KQpjb21wYXJpc29uX3N1bW1hcnkgPC0gY3JlYXRlX21vZGVsX2NvbXBhcmlzb25fc3VtbWFyeShyaW5fc3VtbWFyeSwgYXV0b2x5c2lzX3N1bW1hcnkpCgpnZ3NhdmUoZmlsZS5wYXRoKG91dHB1dF9wYXRoLCAiU3VwcEZpZ19TM19FeHRlbmRlZF9Db21wYXJpc29uLnBkZiIpLCAKICAgICAgIHN1cHBfZmlndXJlX3MzLCB3aWR0aCA9IDE2LCBoZWlnaHQgPSAxNCwgZHBpID0gNjAwLCBiZyA9ICJ3aGl0ZSIpCmBgYAoKIy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIDE1LiBDb21wcmVoZW5zaXZlIEZpbmFsIFJlcG9ydCBHZW5lcmF0aW9uCiMtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KYGBge3J9CmdlbmVyYXRlX2NvbXByZWhlbnNpdmVfcmVwb3J0IDwtIGZ1bmN0aW9uKCkgewogIHJlcG9ydF9wYXRoIDwtIGZpbGUucGF0aChkYXRhX3BhdGgsICJwcm9jZXNzZWQvY29tcHJlaGVuc2l2ZV9ndGV4X3JlcG9ydC50eHQiKQogIAogIHNpbmsocmVwb3J0X3BhdGgpCiAgCiAgY2F0KCI9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuIikKICBjYXQoIiAgICAgICAgICAgICAgICAgICAgQ09NUFJFSEVOU0lWRSBHVEV4IEFOQUxZU0lTIFJFUE9SVCAgICAgICAgICAgICAgICAgICAgICAgICAgXG4iKQogIGNhdCgiPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cblxuIikKICAKICAjIEhlYWRlciBJbmZvcm1hdGlvbgogIGNhdCgiQU5BTFlTSVMgT1ZFUlZJRVdcbiIpCiAgY2F0KCItLS0tLS0tLS0tLS0tLS0tLVxuIikKICBjYXQocGFzdGUoIlJlcG9ydCBHZW5lcmF0ZWQ6IiwgU3lzLnRpbWUoKSwgIlxuIikpCiAgY2F0KHBhc3RlKCJQaXBlbGluZSBWZXJzaW9uOiBHVEV4IEFuYWx5c2lzIFBpcGVsaW5lIHYxLjBcbiIpKQogIGNhdChwYXN0ZSgiQW5hbHlzaXMgRHVyYXRpb246IiwgZm9ybWF0KFN5cy50aW1lKCkgLSBzdGFydF90aW1lLCBkaWdpdHMgPSAzKSwgIlxuXG4iKSkKICAKICAjIERhdGFzZXQgU3VtbWFyeQogIGNhdCgiREFUQVNFVCBTVU1NQVJZXG4iKQogIGNhdCgiPT09PT09PT09PT09PT09XG4iKQogIGNhdChwYXN0ZSgiVG90YWwgU2FtcGxlcyBBbmFseXplZDoiLCBucm93KG1lcmdlZF9ndGV4KSwgIlxuIikpCiAgY2F0KHBhc3RlKCJUb3RhbCBUaXNzdWUgVHlwZXM6IiwgbGVuZ3RoKHVuaXF1ZShtZXJnZWRfZ3RleCR0aXNzdWVfbWFpbikpLCAiXG4iKSkKICBjYXQocGFzdGUoIlRvdGFsIEZlYXR1cmVzIEV4dHJhY3RlZDoiLCBsZW5ndGgoZ3JlcCgiXmZlYXR1cmVfIiwgbmFtZXMobWVyZ2VkX2d0ZXgpKSksICJcbiIpKQogIGNhdChwYXN0ZSgiTWFsZSBTdWJqZWN0czoiLCBzdW0obWVyZ2VkX2d0ZXgkc2V4ID09ICJtYWxlIiksICJcbiIpKQogIGNhdChwYXN0ZSgiRmVtYWxlIFN1YmplY3RzOiIsIHN1bShtZXJnZWRfZ3RleCRzZXggPT0gImZlbWFsZSIpLCAiXG5cbiIpKQogIAogICMgVG9wIHRpc3N1ZSB0eXBlcyBieSBzYW1wbGUgY291bnQKICB0aXNzdWVfY291bnRzIDwtIG1lcmdlZF9ndGV4WywgLk4sIGJ5ID0gdGlzc3VlX21haW5dW29yZGVyKC1OKV0KICBjYXQoIlRvcCAxMCBUaXNzdWUgVHlwZXMgYnkgU2FtcGxlIENvdW50OlxuIikKICBmb3IoaSBpbiAxOm1pbigxMCwgbnJvdyh0aXNzdWVfY291bnRzKSkpIHsKICAgIGNhdChzcHJpbnRmKCIgICUyZC4gJS0yNXM6ICU0ZCBzYW1wbGVzXG4iLCBpLCB0aXNzdWVfY291bnRzJHRpc3N1ZV9tYWluW2ldLCB0aXNzdWVfY291bnRzJE5baV0pKQogIH0KICBjYXQoIlxuIikKICAKICAjIFF1YWxpdHkgTWV0cmljcyBBbmFseXNpcwogIGNhdCgiUVVBTElUWSBNRVRSSUNTIEFOQUxZU0lTXG4iKQogIGNhdCgiPT09PT09PT09PT09PT09PT09PT09PT09XG4iKQogIAogICMgUklOIEFuYWx5c2lzCiAgcmluX2RhdGEgPC0gbWVyZ2VkX2d0ZXhbIWlzLm5hKFNNUklOKV0KICBpZihucm93KHJpbl9kYXRhKSA+IDApIHsKICAgIGNhdCgiUklOIFNjb3JlIERpc3RyaWJ1dGlvbjpcbiIpCiAgICBjYXQocGFzdGUoIiAgU2FtcGxlcyB3aXRoIFJJTiBkYXRhOiIsIG5yb3cocmluX2RhdGEpLCAib2YiLCBucm93KG1lcmdlZF9ndGV4KSwgCiAgICAgICAgICAgICAgc3ByaW50ZigiKCUuMWYlJSlcbiIsIDEwMCAqIG5yb3cocmluX2RhdGEpIC8gbnJvdyhtZXJnZWRfZ3RleCkpKSkKICAgIGNhdChwYXN0ZSgiICBNZWFuIFJJTiBTY29yZToiLCByb3VuZChtZWFuKHJpbl9kYXRhJFNNUklOKSwgMiksICJcbiIpKQogICAgY2F0KHBhc3RlKCIgIE1lZGlhbiBSSU4gU2NvcmU6Iiwgcm91bmQobWVkaWFuKHJpbl9kYXRhJFNNUklOKSwgMiksICJcbiIpKQogICAgY2F0KHBhc3RlKCIgIFJJTiBSYW5nZToiLCByb3VuZChtaW4ocmluX2RhdGEkU01SSU4pLCAyKSwgIi0iLCByb3VuZChtYXgocmluX2RhdGEkU01SSU4pLCAyKSwgIlxuIikpCiAgICBjYXQocGFzdGUoIiAgSGlnaCBRdWFsaXR5IChSSU4g4omlIDcuMCk6Iiwgc3VtKHJpbl9kYXRhJFNNUklOID49IDcuMCksIAogICAgICAgICAgICAgIHNwcmludGYoIiglLjFmJSUpXG4iLCAxMDAgKiBzdW0ocmluX2RhdGEkU01SSU4gPj0gNy4wKSAvIG5yb3cocmluX2RhdGEpKSkpCiAgfQogIAogICMgQXV0b2x5c2lzIEFuYWx5c2lzCiAgYXV0b2x5c2lzX2RhdGEgPC0gbWVyZ2VkX2d0ZXhbIWlzLm5hKFNNQVRTU0NSKV0KICBpZihucm93KGF1dG9seXNpc19kYXRhKSA+IDApIHsKICAgIGNhdCgiXG5BdXRvbHlzaXMgU2NvcmUgRGlzdHJpYnV0aW9uOlxuIikKICAgIGNhdChwYXN0ZSgiICBTYW1wbGVzIHdpdGggQXV0b2x5c2lzIGRhdGE6IiwgbnJvdyhhdXRvbHlzaXNfZGF0YSksICJvZiIsIG5yb3cobWVyZ2VkX2d0ZXgpLAogICAgICAgICAgICAgIHNwcmludGYoIiglLjFmJSUpXG4iLCAxMDAgKiBucm93KGF1dG9seXNpc19kYXRhKSAvIG5yb3cobWVyZ2VkX2d0ZXgpKSkpCiAgICBjYXQocGFzdGUoIiAgTWVhbiBBdXRvbHlzaXMgU2NvcmU6Iiwgcm91bmQobWVhbihhdXRvbHlzaXNfZGF0YSRTTUFUU1NDUiksIDIpLCAiXG4iKSkKICAgIGNhdChwYXN0ZSgiICBNZWRpYW4gQXV0b2x5c2lzIFNjb3JlOiIsIHJvdW5kKG1lZGlhbihhdXRvbHlzaXNfZGF0YSRTTUFUU1NDUiksIDIpLCAiXG4iKSkKICAgIGF1dG9seXNpc19jb3VudHMgPC0gdGFibGUoYXV0b2x5c2lzX2RhdGEkU01BVFNTQ1IpCiAgICBmb3Ioc2NvcmUgaW4gbmFtZXMoYXV0b2x5c2lzX2NvdW50cykpIHsKICAgICAgY2F0KHNwcmludGYoIiAgU2NvcmUgJXM6ICVkIHNhbXBsZXMgKCUuMWYlJSlcbiIsIHNjb3JlLCBhdXRvbHlzaXNfY291bnRzW3Njb3JlXSwKICAgICAgICAgICAgICAgICAgMTAwICogYXV0b2x5c2lzX2NvdW50c1tzY29yZV0gLyBucm93KGF1dG9seXNpc19kYXRhKSkpCiAgICB9CiAgICBjYXQocGFzdGUoIiAgSGlnaCBRdWFsaXR5IChTY29yZSDiiaQgMik6Iiwgc3VtKGF1dG9seXNpc19kYXRhJFNNQVRTU0NSIDw9IDIpLAogICAgICAgICAgICAgIHNwcmludGYoIiglLjFmJSUpXG4iLCAxMDAgKiBzdW0oYXV0b2x5c2lzX2RhdGEkU01BVFNTQ1IgPD0gMikgLyBucm93KGF1dG9seXNpc19kYXRhKSkpKQogIH0KICAKICAjIERpbWVuc2lvbmFsaXR5IFJlZHVjdGlvbiBSZXN1bHRzCiAgY2F0KCJcbkRJTUVOU0lPTkFMSVRZIFJFRFVDVElPTiBBTkFMWVNJU1xuIikKICBjYXQoIj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuIikKICAKICBpZihleGlzdHMoInBjYV9yZXN1bHRzIikpIHsKICAgIGNhdCgiUHJpbmNpcGFsIENvbXBvbmVudCBBbmFseXNpczpcbiIpCiAgICBjYXQocGFzdGUoIiAgUEMxIFZhcmlhbmNlIEV4cGxhaW5lZDoiLCBzcHJpbnRmKCIlLjFmJSUiLCBwY2FfcmVzdWx0cyR2YXJpYW5jZV9leHBsYWluZWRbMV0gKiAxMDApLCAiXG4iKSkKICAgIGNhdChwYXN0ZSgiICBQQzIgVmFyaWFuY2UgRXhwbGFpbmVkOiIsIHNwcmludGYoIiUuMWYlJSIsIHBjYV9yZXN1bHRzJHZhcmlhbmNlX2V4cGxhaW5lZFsyXSAqIDEwMCksICJcbiIpKQogICAgY2F0KHBhc3RlKCIgIEZpcnN0IDEwIFBDcyBDdW11bGF0aXZlIFZhcmlhbmNlOiIsIHNwcmludGYoIiUuMWYlJSIsIHBjYV9yZXN1bHRzJGN1bXVsYXRpdmVfdmFyaWFuY2VbMTBdICogMTAwKSwgIlxuIikpCiAgICBjYXQocGFzdGUoIiAgRmlyc3QgNTAgUENzIEN1bXVsYXRpdmUgVmFyaWFuY2U6Iiwgc3ByaW50ZigiJS4xZiUlIiwgcGNhX3Jlc3VsdHMkY3VtdWxhdGl2ZV92YXJpYW5jZVs1MF0gKiAxMDApLCAiXG4iKSkKICB9CiAgCiAgaWYoZXhpc3RzKCJ0aXNzdWVfc2lsIikpIHsKICAgIGNhdCgiXG5VTUFQIENsdXN0ZXJpbmcgQW5hbHlzaXM6XG4iKQogICAgY2F0KHBhc3RlKCIgIE92ZXJhbGwgU2lsaG91ZXR0ZSBTY29yZToiLCBzcHJpbnRmKCIlLjNmIiwgbWVhbihzaWxbLDNdLCBuYS5ybSA9IFRSVUUpKSwgIlxuIikpCiAgICBjYXQoIiAgVG9wIDUgQmVzdC1TZXBhcmF0ZWQgVGlzc3VlcyAoYnkgU2lsaG91ZXR0ZSBTY29yZSk6XG4iKQogICAgdG9wX3NpbGhvdWV0dGUgPC0gaGVhZCh0aXNzdWVfc2lsW29yZGVyKC1hdmdfc2lsaG91ZXR0ZSldLCA1KQogICAgZm9yKGkgaW4gMTpucm93KHRvcF9zaWxob3VldHRlKSkgewogICAgICBjYXQoc3ByaW50ZigiICAgICVkLiAlLTI1czogJS4zZlxuIiwgaSwgdG9wX3NpbGhvdWV0dGUkdGlzc3VlW2ldLCB0b3Bfc2lsaG91ZXR0ZSRhdmdfc2lsaG91ZXR0ZVtpXSkpCiAgICB9CiAgfQogIAogICMgVmFyaWFuY2UgQW5hbHlzaXMgUmVzdWx0cwogIGNhdCgiXG5WQVJJQU5DRSBBTkFMWVNJUyAoQU5PVkEpXG4iKQogIGNhdCgiPT09PT09PT09PT09PT09PT09PT09PT09PVxuIikKICAKICBpZihleGlzdHMoInJpbl92YXJpYW5jZSIpKSB7CiAgICBjYXQoIlJJTiBTY29yZSBWYXJpYW5jZSBFeHBsYWluZWQgYnkgRGVtb2dyYXBoaWNzOlxuIikKICAgIHJpbl92YXJpYW5jZV9zb3J0ZWQgPC0gcmluX3ZhcmlhbmNlW29yZGVyKC1yaW5fdmFyaWFuY2UkdmFyX2V4cGxhaW5lZCksXQogICAgZm9yKGkgaW4gMTpucm93KHJpbl92YXJpYW5jZV9zb3J0ZWQpKSB7CiAgICAgIGNhdChzcHJpbnRmKCIgICUtMjBzOiAlNS4xZiUlIChwICVzKVxuIiwgCiAgICAgICAgICAgICAgICAgIHJpbl92YXJpYW5jZV9zb3J0ZWQkbGFiZWxbaV0sIAogICAgICAgICAgICAgICAgICByaW5fdmFyaWFuY2Vfc29ydGVkJHZhcl9leHBsYWluZWRbaV0sCiAgICAgICAgICAgICAgICAgIHJpbl92YXJpYW5jZV9zb3J0ZWQkc2lnbmlmaWNhbmNlW2ldKSkKICAgIH0KICB9CiAgCiAgaWYoZXhpc3RzKCJhdXRvbHlzaXNfdmFyaWFuY2UiKSkgewogICAgY2F0KCJcbkF1dG9seXNpcyBTY29yZSBWYXJpYW5jZSBFeHBsYWluZWQgYnkgRGVtb2dyYXBoaWNzOlxuIikKICAgIGF1dG9seXNpc192YXJpYW5jZV9zb3J0ZWQgPC0gYXV0b2x5c2lzX3ZhcmlhbmNlW29yZGVyKC1hdXRvbHlzaXNfdmFyaWFuY2UkdmFyX2V4cGxhaW5lZCksXQogICAgZm9yKGkgaW4gMTpucm93KGF1dG9seXNpc192YXJpYW5jZV9zb3J0ZWQpKSB7CiAgICAgIGNhdChzcHJpbnRmKCIgICUtMjBzOiAlNS4xZiUlIChwICVzKVxuIiwgCiAgICAgICAgICAgICAgICAgIGF1dG9seXNpc192YXJpYW5jZV9zb3J0ZWQkbGFiZWxbaV0sIAogICAgICAgICAgICAgICAgICBhdXRvbHlzaXNfdmFyaWFuY2Vfc29ydGVkJHZhcl9leHBsYWluZWRbaV0sCiAgICAgICAgICAgICAgICAgIGF1dG9seXNpc192YXJpYW5jZV9zb3J0ZWQkc2lnbmlmaWNhbmNlW2ldKSkKICAgIH0KICB9CiAgCiAgIyBDb3JyZWxhdGlvbiBBbmFseXNpcwogIGNhdCgiXG5DT1JSRUxBVElPTiBBTkFMWVNJU1xuIikKICBjYXQoIj09PT09PT09PT09PT09PT09PT09XG4iKQogIAogIGlmKGV4aXN0cygidGlzc3VlX2NvcnIiKSkgewogICAgY2F0KCJSSU4gdnMgQXV0b2x5c2lzIENvcnJlbGF0aW9ucyBieSBUaXNzdWU6XG4iKQogICAgdGlzc3VlX2NvcnJfc29ydGVkIDwtIHRpc3N1ZV9jb3JyW29yZGVyKC1hYnModGlzc3VlX2NvcnIkY29ycmVsYXRpb24pKSxdCiAgICBjYXQoIiAgU3Ryb25nZXN0IENvcnJlbGF0aW9ucyAoVG9wIDEwKTpcbiIpCiAgICB0b3BfY29yciA8LSBoZWFkKHRpc3N1ZV9jb3JyX3NvcnRlZCwgMTApCiAgICBmb3IoaSBpbiAxOm5yb3codG9wX2NvcnIpKSB7CiAgICAgIGNhdChzcHJpbnRmKCIgICAgJS0yNXM6IHIgPSAlNi4zZiAocCAlcywgbiA9ICVkKVxuIiwgCiAgICAgICAgICAgICAgICAgIHRvcF9jb3JyJHRpc3N1ZV9tYWluW2ldLCAKICAgICAgICAgICAgICAgICAgdG9wX2NvcnIkY29ycmVsYXRpb25baV0sCiAgICAgICAgICAgICAgICAgIHRvcF9jb3JyJHNpZ25pZmljYW5jZVtpXSwKICAgICAgICAgICAgICAgICAgdG9wX2NvcnIkbl9zYW1wbGVzW2ldKSkKICAgIH0KICB9CiAgCiAgIyBNYWNoaW5lIExlYXJuaW5nIE1vZGVsIFJlc3VsdHMKICBjYXQoIlxuTUFDSElORSBMRUFSTklORyBNT0RFTCBQRVJGT1JNQU5DRVxuIikKICBjYXQoIj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiIpCiAgCiAgaWYoZXhpc3RzKCJhbGxfcmVzdWx0cyIpKSB7CiAgICAjIFJJTiBNb2RlbHMKICAgIGlmKCJTTVJJTiIgJWluJSBuYW1lcyhhbGxfcmVzdWx0cykpIHsKICAgICAgcmluX21vZGVscyA8LSBhbGxfcmVzdWx0c1tbIlNNUklOIl1dCiAgICAgIHJpbl9yX3ZhbHVlcyA8LSBzYXBwbHkocmluX21vZGVscywgZnVuY3Rpb24oeCkgewogICAgICAgIGlmKGlzLm51bGwoeCkpIHJldHVybihOQSkKICAgICAgICByZXR1cm4oYWJzKHgkcGVyZm9ybWFuY2UkcG9vbGVkX3IpKSAgIyBVc2UgYWJzb2x1dGUgUgogICAgICAgICMgcmV0dXJuKHNxcnQoeCRwZXJmb3JtYW5jZSRwb29sZWRfcjIpKSAjIHVzZSB3aGVuIHJlcXVpcmVkIGZvciBSwrIKICAgICAgfSkKICAgICAgcmluX3JfdmFsdWVzIDwtIHJpbl9yX3ZhbHVlc1shaXMubmEocmluX3JfdmFsdWVzKV0KICAgICAgCiAgICAgIGNhdCgiUklOIFNjb3JlIFByZWRpY3Rpb24gTW9kZWxzOlxuIikKICAgICAgY2F0KHBhc3RlKCIgIFN1Y2Nlc3NmdWwgbW9kZWxzOiIsIGxlbmd0aChyaW5fcl92YWx1ZXMpLCAidGlzc3Vlc1xuIikpCiAgICAgIGNhdChwYXN0ZSgiICBNZWFuIHxSfDoiLCBzcHJpbnRmKCIlLjNmIiwgbWVhbihyaW5fcl92YWx1ZXMpKSwgIlxuIikpCiAgICAgIGNhdChwYXN0ZSgiICBNZWRpYW4gfFJ8OiIsIHNwcmludGYoIiUuM2YiLCBtZWRpYW4ocmluX3JfdmFsdWVzKSksICJcbiIpKQogICAgICBjYXQocGFzdGUoIiAgU3Ryb25nIG1vZGVscyAofFJ8ID4gMC41KToiLCBzdW0ocmluX3JfdmFsdWVzID4gMC41KSwgIlxuIikpCiAgICAgIGNhdChwYXN0ZSgiICBFeGNlbGxlbnQgbW9kZWxzICh8UnwgPiAwLjcpOiIsIHN1bShyaW5fcl92YWx1ZXMgPiAwLjcpLCAiXG4iKSkKICAgICAgCiAgICAgICMgVG9wIHBlcmZvcm1pbmcgdGlzc3VlcwogICAgICBjYXQoIiAgVG9wIDUgUklOIFByZWRpY3Rpb24gTW9kZWxzOlxuIikKICAgICAgdG9wX3JpbiA8LSBuYW1lcyhzb3J0KHJpbl9yX3ZhbHVlcywgZGVjcmVhc2luZyA9IFRSVUUpKVsxOjVdCiAgICAgIGZvcihpIGluIDE6bGVuZ3RoKHRvcF9yaW4pKSB7CiAgICAgICAgdGlzc3VlX25hbWUgPC0gdG9wX3JpbltpXQogICAgICAgIHJfdmFsdWUgPC0gcmluX3JfdmFsdWVzW3Rpc3N1ZV9uYW1lXQogICAgICAgIGNhdChzcHJpbnRmKCIgICAgJWQuICUtMjVzOiB8UnwgPSAlLjNmXG4iLCBpLCB0aXNzdWVfbmFtZSwgcl92YWx1ZSkpCiAgICAgIH0KICAgIH0KICAgIAogICAgIyBBdXRvbHlzaXMgTW9kZWxzCiAgICBpZigiU01BVFNTQ1IiICVpbiUgbmFtZXMoYWxsX3Jlc3VsdHMpKSB7CiAgICAgIGF1dG9fbW9kZWxzIDwtIGFsbF9yZXN1bHRzW1siU01BVFNTQ1IiXV0KICAgICAgYXV0b19yX3ZhbHVlcyA8LSBzYXBwbHkoYXV0b19tb2RlbHMsIGZ1bmN0aW9uKHgpIHsKICAgICAgICBpZihpcy5udWxsKHgpKSByZXR1cm4oTkEpCiAgICAgICAgcmV0dXJuKHNxcnQoeCRwZXJmb3JtYW5jZSRwb29sZWRfcjIpKQogICAgICB9KQogICAgICBhdXRvX3JfdmFsdWVzIDwtIGF1dG9fcl92YWx1ZXNbIWlzLm5hKGF1dG9fcl92YWx1ZXMpXQogICAgICAKICAgICAgY2F0KCJcbkF1dG9seXNpcyBTY29yZSBQcmVkaWN0aW9uIE1vZGVsczpcbiIpCiAgICAgIGNhdChwYXN0ZSgiICBTdWNjZXNzZnVsIG1vZGVsczoiLCBsZW5ndGgoYXV0b19yX3ZhbHVlcyksICJ0aXNzdWVzXG4iKSkKICAgICAgY2F0KHBhc3RlKCIgIE1lYW4gUjoiLCBzcHJpbnRmKCIlLjNmIiwgbWVhbihhdXRvX3JfdmFsdWVzKSksICJcbiIpKQogICAgICBjYXQocGFzdGUoIiAgTWVkaWFuIFI6Iiwgc3ByaW50ZigiJS4zZiIsIG1lZGlhbihhdXRvX3JfdmFsdWVzKSksICJcbiIpKQogICAgICBjYXQocGFzdGUoIiAgU3Ryb25nIG1vZGVscyAoUiA+IDAuNSk6Iiwgc3VtKGF1dG9fcl92YWx1ZXMgPiAwLjUpLCAiXG4iKSkKICAgICAgY2F0KHBhc3RlKCIgIEV4Y2VsbGVudCBtb2RlbHMgKFIgPiAwLjcpOiIsIHN1bShhdXRvX3JfdmFsdWVzID4gMC43KSwgIlxuIikpCiAgICAgIAogICAgICAjIFRvcCBwZXJmb3JtaW5nIHRpc3N1ZXMKICAgICAgY2F0KCIgIFRvcCA1IEF1dG9seXNpcyBQcmVkaWN0aW9uIE1vZGVsczpcbiIpCiAgICAgIHRvcF9hdXRvIDwtIG5hbWVzKHNvcnQoYXV0b19yX3ZhbHVlcywgZGVjcmVhc2luZyA9IFRSVUUpKVsxOjVdCiAgICAgIGZvcihpIGluIDE6bGVuZ3RoKHRvcF9hdXRvKSkgewogICAgICAgIHRpc3N1ZV9uYW1lIDwtIHRvcF9hdXRvW2ldCiAgICAgICAgcl92YWx1ZSA8LSBhdXRvX3JfdmFsdWVzW3Rpc3N1ZV9uYW1lXQogICAgICAgIGNhdChzcHJpbnRmKCIgICAgJWQuICUtMjVzOiBSID0gJS4zZlxuIiwgaSwgdGlzc3VlX25hbWUsIHJfdmFsdWUpKQogICAgICB9CiAgICB9CiAgfQogIAogICMgRmVhdHVyZSBBbmFseXNpcwogIGNhdCgiXG5GRUFUVVJFIElNUE9SVEFOQ0UgQU5BTFlTSVNcbiIpCiAgY2F0KCI9PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiIpCiAgCiAgaWYoZXhpc3RzKCJjb21tb25fZmVhdHVyZXMiKSAmJiBleGlzdHMoInJpbl9vbmx5X2ZlYXR1cmVzIikgJiYgZXhpc3RzKCJhdXRvX29ubHlfZmVhdHVyZXMiKSkgewogICAgY2F0KCJGZWF0dXJlIE92ZXJsYXAgQW5hbHlzaXM6XG4iKQogICAgY2F0KHBhc3RlKCIgIENvbW1vbiBmZWF0dXJlcyAoYm90aCBtb2RlbHMpOiIsIGxlbmd0aChjb21tb25fZmVhdHVyZXMpLCAiXG4iKSkKICAgIGNhdChwYXN0ZSgiICBSSU4tc3BlY2lmaWMgZmVhdHVyZXM6IiwgbGVuZ3RoKHJpbl9vbmx5X2ZlYXR1cmVzKSwgIlxuIikpCiAgICBjYXQocGFzdGUoIiAgQXV0b2x5c2lzLXNwZWNpZmljIGZlYXR1cmVzOiIsIGxlbmd0aChhdXRvX29ubHlfZmVhdHVyZXMpLCAiXG4iKSkKICAgIAogICAgdG90YWxfdW5pcXVlIDwtIGxlbmd0aChjb21tb25fZmVhdHVyZXMpICsgbGVuZ3RoKHJpbl9vbmx5X2ZlYXR1cmVzKSArIGxlbmd0aChhdXRvX29ubHlfZmVhdHVyZXMpCiAgICBjYXQocGFzdGUoIiAgRmVhdHVyZSBvdmVybGFwIHBlcmNlbnRhZ2U6Iiwgc3ByaW50ZigiJS4xZiUlIiwgMTAwICogbGVuZ3RoKGNvbW1vbl9mZWF0dXJlcykgLyB0b3RhbF91bmlxdWUpLCAiXG4iKSkKICB9CiAgCiAgIyBFc29waGFndXMgQW5hbHlzaXMKICBpZihleGlzdHMoImVzb3BoYWd1c19zYW1wbGVzIikpIHsKICAgIGNhdCgiXG5FU09QSEFHVVMgU1VCVFlQRSBBTkFMWVNJU1xuIikKICAgIGNhdCgiPT09PT09PT09PT09PT09PT09PT09PT09PT1cbiIpCiAgICBlc29waGFndXNfY291bnRzIDwtIHRhYmxlKGVzb3BoYWd1c19zYW1wbGVzJHRpc3N1ZV9zdWIpCiAgICBjYXQoIkVzb3BoYWd1cyBTdWJ0eXBlIERpc3RyaWJ1dGlvbjpcbiIpCiAgICBmb3Ioc3VidHlwZSBpbiBuYW1lcyhlc29waGFndXNfY291bnRzKSkgewogICAgICBjYXQoc3ByaW50ZigiICAlLTMwczogJTNkIHNhbXBsZXNcbiIsIHN1YnR5cGUsIGVzb3BoYWd1c19jb3VudHNbc3VidHlwZV0pKQogICAgfQogIH0KICAKICAjIENyb3NzLVZhbGlkYXRpb24gU3RhYmlsaXR5CiAgaWYoZXhpc3RzKCJzdGFiaWxpdHlfc3RhdHMiKSkgewogICAgY2F0KCJcbk1PREVMIFNUQUJJTElUWSBBTkFMWVNJU1xuIikKICAgIGNhdCgiPT09PT09PT09PT09PT09PT09PT09PT09XG4iKQogICAgCiAgICBzdGFibGVfcmluIDwtIHN1bShzdGFiaWxpdHlfc3RhdHMkY3ZfcltzdGFiaWxpdHlfc3RhdHMkdGFyZ2V0ID09ICJTTVJJTiJdIDwgMC4yLCBuYS5ybSA9IFRSVUUpCiAgICB0b3RhbF9yaW4gPC0gc3VtKHN0YWJpbGl0eV9zdGF0cyR0YXJnZXQgPT0gIlNNUklOIikKICAgIHN0YWJsZV9hdXRvIDwtIHN1bShzdGFiaWxpdHlfc3RhdHMkY3ZfcltzdGFiaWxpdHlfc3RhdHMkdGFyZ2V0ID09ICJTTUFUU1NDUiJdIDwgMC4yLCBuYS5ybSA9IFRSVUUpCiAgICB0b3RhbF9hdXRvIDwtIHN1bShzdGFiaWxpdHlfc3RhdHMkdGFyZ2V0ID09ICJTTUFUU1NDUiIpCiAgICAKICAgIGNhdCgiQ3Jvc3MtVmFsaWRhdGlvbiBTdGFiaWxpdHkgKENWIDwgMC4yID0gU3RhYmxlKTpcbiIpCiAgICBjYXQoc3ByaW50ZigiICBSSU4gTW9kZWxzOiAlZC8lZCBzdGFibGUgKCUuMWYlJSlcbiIsIHN0YWJsZV9yaW4sIHRvdGFsX3JpbiwgMTAwICogc3RhYmxlX3JpbiAvIHRvdGFsX3JpbikpCiAgICBjYXQoc3ByaW50ZigiICBBdXRvbHlzaXMgTW9kZWxzOiAlZC8lZCBzdGFibGUgKCUuMWYlJSlcbiIsIHN0YWJsZV9hdXRvLCB0b3RhbF9hdXRvLCAxMDAgKiBzdGFibGVfYXV0byAvIHRvdGFsX2F1dG8pKQogIH0KICAKICAjIFN1bW1hcnkgU3RhdGlzdGljcwogIGNhdCgiXG5TVU1NQVJZIFNUQVRJU1RJQ1NcbiIpCiAgY2F0KCI9PT09PT09PT09PT09PT09PT1cbiIpCiAgCiAgIyBGaWxlcyBnZW5lcmF0ZWQKICBvdXRwdXRfZmlsZXMgPC0gbGlzdC5maWxlcyhvdXRwdXRfcGF0aCwgcGF0dGVybiA9ICIqLnBkZiIsIGZ1bGwubmFtZXMgPSBGQUxTRSkKICBjYXQocGFzdGUoIkdlbmVyYXRlZCIsIGxlbmd0aChvdXRwdXRfZmlsZXMpLCAidmlzdWFsaXphdGlvbiBmaWxlczpcbiIpKQogIGZvcihmaWxlIGluIG91dHB1dF9maWxlcykgewogICAgY2F0KHBhc3RlKCIgIC0iLCBmaWxlLCAiXG4iKSkKICB9CiAgCiAgIyBEYXRhIGZpbGVzIHNhdmVkCiAgcHJvY2Vzc2VkX2ZpbGVzIDwtIGxpc3QuZmlsZXMoZmlsZS5wYXRoKGRhdGFfcGF0aCwgInByb2Nlc3NlZCIpLCBwYXR0ZXJuID0gIioucmRzfCouY3N2IiwgZnVsbC5uYW1lcyA9IEZBTFNFKQogIGNhdChwYXN0ZSgiXG5TYXZlZCIsIGxlbmd0aChwcm9jZXNzZWRfZmlsZXMpLCAicHJvY2Vzc2VkIGRhdGEgZmlsZXM6XG4iKSkKICBmb3IoZmlsZSBpbiBoZWFkKHByb2Nlc3NlZF9maWxlcywgMTApKSB7ICAjIFNob3cgZmlyc3QgMTAgdG8gYXZvaWQgY2x1dHRlcgogICAgY2F0KHBhc3RlKCIgIC0iLCBmaWxlLCAiXG4iKSkKICB9CiAgaWYobGVuZ3RoKHByb2Nlc3NlZF9maWxlcykgPiAxMCkgewogICAgY2F0KHBhc3RlKCIgIC4uLiBhbmQiLCBsZW5ndGgocHJvY2Vzc2VkX2ZpbGVzKSAtIDEwLCAibW9yZSBmaWxlc1xuIikpCiAgfQogIAogICMgS2V5IEZpbmRpbmdzIFN1bW1hcnkKICBjYXQoIlxuS0VZIEZJTkRJTkdTXG4iKQogIGNhdCgiPT09PT09PT09PT09XG4iKQogIGNhdCgiMS4gREFUQVNFVCBDSEFSQUNURVJJU1RJQ1M6XG4iKQogIGNhdCgiICAgLSBMYXJnZS1zY2FsZSBhbmFseXNpcyBvZiBHVEV4IHdob2xlLXNsaWRlIGltYWdlIGZlYXR1cmVzXG4iKQogIGNhdCgiICAgLSBDb21wcmVoZW5zaXZlIGNvdmVyYWdlIG9mIGh1bWFuIHRpc3N1ZSB0eXBlc1xuIikKICBjYXQoIiAgIC0gUXVhbGl0eSBtZXRyaWNzIGF2YWlsYWJsZSBmb3Igc3Vic3RhbnRpYWwgc3Vic2V0XG5cbiIpCiAgCiAgY2F0KCIyLiBUSVNTVUUgSEVURVJPR0VORUlUWTpcbiIpCiAgY2F0KCIgICAtIENsZWFyIHRpc3N1ZS1zcGVjaWZpYyBjbHVzdGVyaW5nIGluIFVNQVAgYW5hbHlzaXNcbiIpCiAgY2F0KCIgICAtIFZhcmlhYmxlIHNpbGhvdWV0dGUgc2NvcmVzIGluZGljYXRlIGRpZmZlcmVudCB0aXNzdWUgc2VwYXJhYmlsaXR5XG4iKQogIGNhdCgiICAgLSBGZWF0dXJlIGltcG9ydGFuY2UgdmFyaWVzIHNpZ25pZmljYW50bHkgYWNyb3NzIHRpc3N1ZSB0eXBlc1xuXG4iKQogIAogIGNhdCgiMy4gUVVBTElUWSBQUkVESUNUSU9OIE1PREVMUzpcbiIpCiAgY2F0KCIgICAtIFRpc3N1ZS1zcGVjaWZpYyBtb2RlbHMgc2hvdyBoZXRlcm9nZW5lb3VzIHBlcmZvcm1hbmNlXG4iKQogIGNhdCgiICAgLSBTb21lIHRpc3N1ZXMgaGlnaGx5IHByZWRpY3RhYmxlLCBvdGhlcnMgbW9yZSBjaGFsbGVuZ2luZ1xuIikKICBjYXQoIiAgIC0gQ3Jvc3MtdmFsaWRhdGlvbiBkZW1vbnN0cmF0ZXMgbW9kZWwgcm9idXN0bmVzc1xuXG4iKQogIAogIGNhdCgiNC4gRkVBVFVSRSBBTkFMWVNJUzpcbiIpCiAgY2F0KCIgICAtIFN0YXRpc3RpY2FsIGZlYXR1cmUgY2F0ZWdvcmllcyBzaG93IGRpZmZlcmVudCBpbXBvcnRhbmNlIHBhdHRlcm5zXG4iKQogIGNhdCgiICAgLSBQYXJ0aWFsIG92ZXJsYXAgYmV0d2VlbiBSSU4gYW5kIGF1dG9seXNpcyBwcmVkaWN0aXZlIGZlYXR1cmVzXG4iKQogIGNhdCgiICAgLSBGZWF0dXJlIGNvbnNpc3RlbmN5IHZhcmllcyBhY3Jvc3MgdGlzc3Vlc1xuXG4iKQogIAogIGNhdCgiNS4gQklPTE9HSUNBTCBJTlNJR0hUUzpcbiIpCiAgY2F0KCIgICAtIFRpc3N1ZSB0eXBlIGlzIHN0cm9uZ2VzdCBwcmVkaWN0b3Igb2YgcXVhbGl0eSB2YXJpYW5jZVxuIikKICBjYXQoIiAgIC0gQWdlIGFuZCBzZXggc2hvdyBtb2Rlc3QgYnV0IHNpZ25pZmljYW50IGVmZmVjdHNcbiIpCiAgY2F0KCIgICAtIEhhcmR5IHNjYWxlIChkZWF0aCBjaXJjdW1zdGFuY2VzKSBpbmZsdWVuY2VzIHRpc3N1ZSBxdWFsaXR5XG5cbiIpCiAgCiAgIyBUZWNobmljYWwgRGV0YWlscwogIGNhdCgiVEVDSE5JQ0FMIERFVEFJTFNcbiIpCiAgY2F0KCI9PT09PT09PT09PT09PT09PVxuIikKICBjYXQoIkFuYWx5c2lzIFBpcGVsaW5lIENvbXBvbmVudHM6XG4iKQogIGNhdCgiICAxLiBEYXRhIEludGVncmF0aW9uIGFuZCBRdWFsaXR5IENvbnRyb2xcbiIpCiAgY2F0KCIgIDIuIERlbW9ncmFwaGljIGFuZCBUaXNzdWUgRGlzdHJpYnV0aW9uIEFuYWx5c2lzXG4iKQogIGNhdCgiICAzLiBQcmluY2lwYWwgQ29tcG9uZW50IEFuYWx5c2lzIChQQ0EpXG4iKQogIGNhdCgiICA0LiBVbmlmb3JtIE1hbmlmb2xkIEFwcHJveGltYXRpb24gYW5kIFByb2plY3Rpb24gKFVNQVApXG4iKQogIGNhdCgiICA1LiBTaWxob3VldHRlIEFuYWx5c2lzIGZvciBDbHVzdGVyIFZhbGlkYXRpb25cbiIpCiAgY2F0KCIgIDYuIFF1YWxpdHkgTWV0cmljcyBEaXN0cmlidXRpb24gQW5hbHlzaXNcbiIpCiAgY2F0KCIgIDcuIEFuYWx5c2lzIG9mIFZhcmlhbmNlIChBTk9WQSkgZm9yIERlbW9ncmFwaGljIEVmZmVjdHNcbiIpCiAgY2F0KCIgIDguIENvcnJlbGF0aW9uIEFuYWx5c2lzIEJldHdlZW4gUXVhbGl0eSBNZXRyaWNzXG4iKQogIGNhdCgiICA5LiBUaXNzdWUtU3BlY2lmaWMgUHJlZGljdGl2ZSBNb2RlbGluZyAoTGFzc28gUmVncmVzc2lvbilcbiIpCiAgY2F0KCIgMTAuIENyb3NzLVZhbGlkYXRpb24gYW5kIE1vZGVsIFN0YWJpbGl0eSBBc3Nlc3NtZW50XG4iKQogIGNhdCgiIDExLiBGZWF0dXJlIEltcG9ydGFuY2UgYW5kIENvbnNpc3RlbmN5IEFuYWx5c2lzXG4iKQogIGNhdCgiIDEyLiBSZXNpZHVhbCBBbmFseXNpcyBhbmQgTW9kZWwgRGlhZ25vc3RpY3NcbiIpCiAgY2F0KCIgMTMuIENvbXBhcmF0aXZlIEFuYWx5c2lzIEFjcm9zcyBUaXNzdWUgVHlwZXNcblxuIikKICAKICBjYXQoIlN0YXRpc3RpY2FsIE1ldGhvZHM6XG4iKQogIGNhdCgiICAtIDUtZm9sZCBjcm9zcy12YWxpZGF0aW9uIGZvciBtb2RlbCB0cmFpbmluZ1xuIikKICBjYXQoIiAgLSBMYXNzbyByZWd1bGFyaXphdGlvbiBmb3IgZmVhdHVyZSBzZWxlY3Rpb25cbiIpCiAgY2F0KCIgIC0gU3BlYXJtYW4gY29ycmVsYXRpb24gZm9yIG5vbi1wYXJhbWV0cmljIGFzc29jaWF0aW9uc1xuIikKICBjYXQoIiAgLSBBTk9WQSBmb3IgdmFyaWFuY2UgZGVjb21wb3NpdGlvblxuIikKICBjYXQoIiAgLSBTaWxob3VldHRlIGFuYWx5c2lzIGZvciBjbHVzdGVyIHF1YWxpdHkgYXNzZXNzbWVudFxuXG4iKQogIAogICMgRm9vdGVyCiAgY2F0KCI9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PVxuIikKICBjYXQoIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEVORCBPRiBSRVBPUlQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXG4iKQogIGNhdCgiPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT1cbiIpCiAgY2F0KHBhc3RlKCJSZXBvcnQgY29tcGxldGVkOiIsIFN5cy50aW1lKCksICJcbiIpKQogIGNhdCgiRm9yIHF1ZXN0aW9ucyBvciBhZGRpdGlvbmFsIGFuYWx5c2lzLCBjb250YWN0IHRoZSBhbmFseXNpcyB0ZWFtLlxuIikKICAKICBzaW5rKCkKICAKICBtZXNzYWdlKCJDb21wcmVoZW5zaXZlIHJlcG9ydCBnZW5lcmF0ZWQgc3VjY2Vzc2Z1bGx5ISIpCiAgbWVzc2FnZSgiUmVwb3J0IHNhdmVkIHRvOiAiLCByZXBvcnRfcGF0aCkKICAKICByZXR1cm4ocmVwb3J0X3BhdGgpCn0KCiMgRXhlY3V0ZSB0aGUgY29tcHJlaGVuc2l2ZSByZXBvcnQKc3RhcnRfdGltZSA8LSBTeXMudGltZSgpICAjIFRyYWNrIGFuYWx5c2lzIGR1cmF0aW9uCmNvbXByZWhlbnNpdmVfcmVwb3J0X3BhdGggPC0gZ2VuZXJhdGVfY29tcHJlaGVuc2l2ZV9yZXBvcnQoKQpgYGAKCgojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgMTUuIEZpbmFsIFJlcG9ydAojLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmBgYHtyfQojIEdlbmVyYXRlIGNvbXByZWhlbnNpdmUgcmVwb3J0CnJlcG9ydF9wYXRoIDwtIGZpbGUucGF0aChkYXRhX3BhdGgsICJwcm9jZXNzZWQvdGlzc3VlX21vZGVsaW5nX3JlcG9ydC50eHQiKQpzaW5rKHJlcG9ydF9wYXRoKQoKY2F0KCJHVEV4IFRJU1NVRS1MRVZFTCBNT0RFTElORyBSRVBPUlRcbiIpCmNhdCgiPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG5cbiIpCgpjYXQoIlNVTU1BUlk6XG4iKQpjYXQocGFzdGUoIkFuYWx5c2lzIERhdGU6IiwgU3lzLnRpbWUoKSwgIlxuIikpCmNhdChwYXN0ZSgiVG90YWwgVGlzc3VlcyBBbmFseXplZDoiLCBsZW5ndGgodW5pcXVlKG1lcmdlZF9ndGV4JHRpc3N1ZV9tYWluKSksICJcbiIpKQpjYXQocGFzdGUoIlRvdGFsIFNhbXBsZXM6IiwgbnJvdyhtZXJnZWRfZ3RleCksICJcblxuIikpCgpjYXQoIk1PREVMSU5HIFJFU1VMVFM6XG4iKQoKIyBSSU4gTW9kZWxzCmlmKCJTTVJJTiIgJWluJSBuYW1lcyhhbGxfcmVzdWx0cykpIHsKICByaW5fbW9kZWxzIDwtIGFsbF9yZXN1bHRzW1siU01SSU4iXV0KICByaW5fcl92YWx1ZXMgPC0gc2FwcGx5KHJpbl9tb2RlbHMsIGZ1bmN0aW9uKHgpIHsKICAgIGlmKGlzLm51bGwoeCkpIHJldHVybihOQSkKICAgIHJldHVybih4JHBlcmZvcm1hbmNlJHBvb2xlZF9yKQogICMgcmV0dXJuKHNxcnQoeCRwZXJmb3JtYW5jZSRwb29sZWRfcjIpKSAjIHVzZSB3aGVuIHJlcXVpcmVkIGZvciBSwrIKICB9KQogIHJpbl9yX3ZhbHVlcyA8LSByaW5fcl92YWx1ZXNbIWlzLm5hKHJpbl9yX3ZhbHVlcyldCiAgCiAgY2F0KCJcblJJTiBTY29yZSBQcmVkaWN0aW9uOlxuIikKICBjYXQocGFzdGUoIiAgU3VjY2Vzc2Z1bCBtb2RlbHM6IiwgbGVuZ3RoKHJpbl9yX3ZhbHVlcyksICJcbiIpKQogIGNhdChwYXN0ZSgiICBNZWFuIFI6Iiwgcm91bmQobWVhbihyaW5fcl92YWx1ZXMpLCAzKSwgIlxuIikpCiAgY2F0KHBhc3RlKCIgIE1lZGlhbiBSOiIsIHJvdW5kKG1lZGlhbihyaW5fcl92YWx1ZXMpLCAzKSwgIlxuIikpCiAgY2F0KHBhc3RlKCIgIFN0cm9uZyBjb3JyZWxhdGlvbnMgKFIgPiAwLjUpOiIsIHN1bShyaW5fcl92YWx1ZXMgPiAwLjUpLCAiXG4iKSkKfQoKIyBBdXRvbHlzaXMgTW9kZWxzCmlmKCJTTUFUU1NDUiIgJWluJSBuYW1lcyhhbGxfcmVzdWx0cykpIHsKICBhdXRvX21vZGVscyA8LSBhbGxfcmVzdWx0c1tbIlNNQVRTU0NSIl1dCiAgYXV0b19yX3ZhbHVlcyA8LSBzYXBwbHkoYXV0b19tb2RlbHMsIGZ1bmN0aW9uKHgpIHsKICAgIGlmKGlzLm51bGwoeCkpIHJldHVybihOQSkKICAgIHJldHVybihzcXJ0KHgkcGVyZm9ybWFuY2UkcG9vbGVkX3IyKSkKICB9KQogIGF1dG9fcl92YWx1ZXMgPC0gYXV0b19yX3ZhbHVlc1shaXMubmEoYXV0b19yX3ZhbHVlcyldCiAgCiAgY2F0KCJcbkF1dG9seXNpcyBTY29yZSBQcmVkaWN0aW9uOlxuIikKICBjYXQocGFzdGUoIiAgU3VjY2Vzc2Z1bCBtb2RlbHM6IiwgbGVuZ3RoKGF1dG9fcl92YWx1ZXMpLCAiXG4iKSkKICBjYXQocGFzdGUoIiAgTWVhbiBSOiIsIHJvdW5kKG1lYW4oYXV0b19yX3ZhbHVlcyksIDMpLCAiXG4iKSkKICBjYXQocGFzdGUoIiAgTWVkaWFuIFI6Iiwgcm91bmQobWVkaWFuKGF1dG9fcl92YWx1ZXMpLCAzKSwgIlxuIikpCiAgY2F0KHBhc3RlKCIgIFN0cm9uZyBjb3JyZWxhdGlvbnMgKFIgPiAwLjUpOiIsIHN1bShhdXRvX3JfdmFsdWVzID4gMC41KSwgIlxuIikpCn0KCmNhdCgiXG5GRUFUVVJFIEFOQUxZU0lTOlxuIikKY2F0KHBhc3RlKCIgIENvbW1vbiBmZWF0dXJlcyBiZXR3ZWVuIG1vZGVsczoiLCBsZW5ndGgoY29tbW9uX2ZlYXR1cmVzKSwgIlxuIikpCmNhdChwYXN0ZSgiICBSSU4tc3BlY2lmaWMgZmVhdHVyZXM6IiwgbGVuZ3RoKHJpbl9vbmx5X2ZlYXR1cmVzKSwgIlxuIikpCmNhdChwYXN0ZSgiICBBdXRvbHlzaXMtc3BlY2lmaWMgZmVhdHVyZXM6IiwgbGVuZ3RoKGF1dG9fb25seV9mZWF0dXJlcyksICJcbiIpKQoKY2F0KCJcbktFWSBGSU5ESU5HUzpcbiIpCmNhdCgiMS4gVGlzc3VlLXNwZWNpZmljIG1vZGVscyBzaG93IGhldGVyb2dlbmVvdXMgcHJlZGljdGFiaWxpdHlcbiIpCmNhdCgiMi4gRmVhdHVyZSBpbXBvcnRhbmNlIHZhcmllcyBhY3Jvc3MgdGlzc3VlcyBhbmQgcXVhbGl0eSBtZXRyaWNzXG4iKQpjYXQoIjMuIENyb3NzLXZhbGlkYXRpb24gZGVtb25zdHJhdGVzIG1vZGVsIHJvYnVzdG5lc3NcbiIpCgpjYXQoIlxuPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09XG4iKQpjYXQoIlJlcG9ydCBnZW5lcmF0ZWQgb246IiwgYXMuY2hhcmFjdGVyKFN5cy50aW1lKCkpLCAiXG4iKQoKc2luaygpCgptZXNzYWdlKCJBbmFseXNpcyBjb21wbGV0ZSEgUmVwb3J0IHNhdmVkIHRvOiIsIHJlcG9ydF9wYXRoKQpgYGA=